GitLab: why the approval was refused
When you approve a merge request through the API (POST /projects/:id/merge_requests/:iid/approve), GitLab answers 403, 409 or 401, and the 401 does not say why. Taking an approval back (…/unapprove) answers 404 when there is nothing to take back. All of this is from GitLab's source (lib/api/merge_request_approvals.rb and the approval services), read on October 24, 2026.
Approving: the answers
- 403
{"message":"403 Forbidden"}: your account may not approve merge requests here at all. In the source, approving needs permission to update the merge request as a member of the project; on Premium and Ultimate, a member of a group named in an approval rule may approve too. - 409
SHA does not match HEAD of source branch: …: the request named the commit you reviewed (sha), and the source branch has another head now (GitLab adds the new one after the colon). Someone pushed after you looked. - 401
{"message":"401 Unauthorized"}: GitLab looked at the approval and refused it. A token that is expired or revoked answers 401 too, but withinvalid_token; see what happened to the token.
A token without the api scope is refused with 403 too, but that answer says insufficient_scope; see which scope to add.
Why 401
From the source, these are the reasons, and the message is the same for all of them:
- You have already approved it. Each person approves once.
- It is already merged.
- You created it, and the project has Prevent approval by merge request creator (author) on. On Premium and Ultimate, GitLab's documentation says that by default the creator cannot approve.
- You added commits to it, and the project has Prevent approvals by users who add commits on.
- The project asks approvers to sign in again (Require user re-authentication to approve), and the request did not carry your correct password in
approval_password. When the group or the instance requires SAML authentication, GitLab's documentation says that approving this way always fails.
Taking an approval back: 404
{"message":"404 Not found"} from …/unapprove does not mean the merge request is missing. In the source, it means there was no approval of yours to remove: you had not approved it (or already took it back), it is merged, or it is locked while GitLab merges it.
Where it stands
curl -H "PRIVATE-TOKEN: your-token" "https://gitlab.com/api/v4/projects/group%2Fproject/merge_requests/42/approvals"
The approvals answer has user_has_approved (you have approved it), user_can_approve (you may approve it now) and approved_by. On Premium and Ultimate it also has approvals_left and require_password_to_approve.
What to do
- 409: look at the new commits, then approve the new head.
- 401: read the approvals. If
user_has_approvedis true, your approval is already there. If you created the merge request or added commits to it, and the project prevents that, ask another reviewer. If the project asks approvers to sign in again, approve in GitLab's web interface, which asks for it. - 403: ask a maintainer for a role that may approve, or to be added to an approval rule.
- 404 on unapprove: refresh: there is no approval of yours left to take back.
In IntelliJ IDEA: "Missing reviewer", "Waiting for review from …", "… approved review" and "… is waiting for updates"
The GitLab merge requests built into IntelliJ IDEA list a merge request's reviewers in its status, one line each. In the source (checked October 30, 2026), the lines come from the merge request's reviewers only, with two fields GraphQL gives for each reviewer, approved and reviewed (GitLab: UserMergeRequestInteraction; IntelliJ IDEA: GitLabMergeRequestReviewFlowViewModel.kt, userReviewer.graphql, GitLabMergeRequestDetailsStatusChecksComponentFactory.kt, CodeReviewDetailsStatusComponentFactory.kt, ReviewDetailsUIUtil.kt):
- "Missing reviewer" when the merge request has no reviewers. Approvals from people who are not reviewers do not change it.
- "… approved review" when the reviewer has approved the merge request.
- "… is waiting for updates" when the reviewer has not approved and
reviewedis true. In GitLab's source,reviewedis true only while the reviewer's review state isREVIEWED("Merge request reviewer has reviewed") (GitLab: MergeRequestReviewState, merge_request_interaction.rb, merge_request_reviewer_state.rb). - "Waiting for review from …" in every other case: a reviewer who has not reviewed yet, and also one who requested changes (
REQUESTED_CHANGES), took an approval back (UNAPPROVED) or started a review without submitting it (REVIEW_STARTED). So in IntelliJ IDEA, a reviewer who requested changes reads as a reviewer still to review.
Each reviewer line that is not an approval has an action to remove that reviewer.
GitLab merge requests in IntelliJ IDEA
Merge Request Inbox for GitLab, a plugin by Lesstep for JetBrains IDEs 2024.1 or later, approves the head commit you had on screen, and once you have approved, offers to take the approval back instead. It can also request changes. When GitLab refuses an approval, it says so: for 409, that the source branch changed and to refresh; for 401 or 403, that your role in the project or its settings may not allow it, with GitLab's words; other refusals in GitLab's words. Reading is free; approving, commenting and merging are Pro (US$1.50 a month, with a 30-day trial).
GITLAB is a trademark of GitLab Inc.; IntelliJ IDEA is a trademark of JetBrains s.r.o. This page is not affiliated with or endorsed by GitLab Inc. or JetBrains.
More: GitLab merge refused (405, 409, 422) · GitLab "insufficient_scope" · GitLab 401 "invalid_token" · GitLab 404 "Project Not Found" · GitLab comment or thread refused (400, 403) · GitLab merge request not created (409, 422, 400) · GitLab rebase refused or failed (403, 409) · GitLab merge request list refused (400, 404) · GitLab merge request changes cut short ("1000+", too_large) · GitLab 429 "Retry later" (rate limits) · GitLab "compare_timeout": true (diff limits) · GitLab merge request plugins for IntelliJ IDEA, compared · 日本語