Suggestion apply endpoints return applied: false after applying
Summary
After a successful apply, PUT /suggestions/:id/apply and PUT /suggestions/batch_apply answer 200 OK with "applied": false and "appliable": true for the suggestions they have just applied. The apply did happen: the commit is on the source branch, the discussions API reports the suggestion as applied, and a second apply is refused.
API::Suggestions#apply_suggestions (suggestions.rb#L66-77) presents the records it loaded before Suggestions::ApplyService ran, and the service marks them as applied with update_all (apply_service.rb#L30-38), which does not change records already loaded. The examples in the Suggestions API documentation show the same stale values and name the attribute applicable, while the API returns appliable.
Steps to reproduce
-
In a merge request, add a diff comment with a suggestion, and read the suggestion's
idfromGET /projects/:id/merge_requests/:merge_request_iid/discussions. -
Apply it, as a user who can push to the source branch:
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \ --url "https://gitlab.example.com/api/v4/suggestions/<suggestion_id>/apply" -
Read the discussions again, and apply the same suggestion a second time.
Example Project
Not needed: the steps work in any project with a merge request.
What is the current bug behavior?
The apply answers 200 OK with "appliable": true, "applied": false. The discussions API then reports "appliable": false, "applied": true, and the second apply answers 400 Bad Request with Can't apply this suggestion. batch_apply answers the same way for each suggestion in the batch.
What is the expected correct behavior?
The 200 OK response describes the suggestions as they are after the apply: "applied": true and "appliable": false.
Relevant logs and/or screenshots
Output on GitLab EE 17.6.1 and in a request spec on master
On a self-managed GitLab EE 17.6.1 instance, with a merge request that adds a.txt and a suggestion that changes its first line (17.6.1 does not list suggestions in the notes API, so the id came from the instance's sequence):
== single apply of 3
{"id":3,"from_line":1,"to_line":1,"appliable":true,"applied":false,"from_content":"l1\n","to_content":"L1\n"} HTTP 200
== file on feat after apply
L1
l2
l3
l4
== re-apply 3 (server now considers it applied)
{"message":"Can't apply this suggestion."} HTTP 400
== MR commits
["Apply 1 suggestion(s) to 1 file(s)","add"]On master at f568e0ae, a request spec that applies a suggestion, reads it back through the discussions API and applies it again:
PROBE PUT /suggestions/:id/apply: HTTP 200 {"id":171,"appliable":true,"applied":false}
PROBE GET .../merge_requests/:iid/discussions, suggestions of the notes: HTTP 200 [{"id":171,"appliable":false,"applied":true}]
PROBE source branch head commit title: "Apply 1 suggestion(s) to 1 file(s)"
PROBE PUT /suggestions/:id/apply again: HTTP 400 {"message":"Can't apply this suggestion."}Output of checks
Reproduced on self-managed GitLab EE 17.6.1, and in a request spec on master.
Possible fixes
Load the suggestions again after a successful apply and present those, in API::Suggestions#apply_suggestions, and show the post-apply values under appliable in the documentation. No UI caller reads this response body (the merge request page resolves the discussion, and the EE AI overview conversations panel refetches), so the UI does not change.
I found this while working on gitlab-mcp-server, and I have a merge request with the fix and specs ready.