Skip to content

gh pr merge doesn't exempt actor from rule. #13458

Closed

Description

@marcusber

Describe the bug

I guess this does is not to the gh-tool itself but the api it works against. Please advice if it should be moved somewhere.

gh merge does not let a bot actor to be exempted from a rule that enforces linear history.

Affected version

Run gh version
  gh version
  shell: /usr/bin/bash -e {0}
gh version 2.92.0 (2026-04-28)
https://github.com/cli/cli/releases/tag/v2.92.0

Steps to reproduce the behavior

I have actions to set up merging between two branches. Push occurs on branch source, actions set up a PR to branch target on behalf of the bot. Once a PR is created another actions is executed and sets up auto-merge and then approves it as github actions.
Rules exists for a) At least one approve. b) Critical CI passes.

I have auto merge enabled in the repo. Everything worked just fine and the PR is merged once critical CI passes.

However, I wanted to encourage developers to squash PRs. I introduced the rule Suggest Squash:

  • Require linear history

All developers are always allowed to bypass it.
My bot is marked as Exempt.

This causes my CI to break whenever the merge commit from branch source to branch target is setup for the PR:

Run gh pr merge --merge --auto $PR_NUMBER
GraphQL: Merge method merge commits are not allowed on this repository (enablePullRequestAutoMerge)
Error: Process completed with exit code 1.

If I set up the bot to allow bypass, the command is executed but since rules are not met it is not auto-merged. This could perhaps be solved by #13388.

    steps:
    - name: Generate a token
      id: generate-token
      uses: actions/create-github-app-token@v3
      with:
        client-id: ${{ vars.ROBOT_CLIENT_ID }}
        private-key: ${{ secrets.ROBOT_PRIVATE_KEY }}
        owner: ${{ github.repository_owner }}

    - name: git checkout
      uses: actions/checkout@v6
      with:
        token: ${{ steps.generate-token.outputs.token }}

    - run: gh pr merge --merge --auto $PR_NUMBER
      name: Enable auto-merge for PR
      env:
        GH_TOKEN: ${{ steps.generate-token.outputs.token }}
        PR_NUMBER: ${{ github.event.pull_request.number }}

Expected vs actual behavior

I expect the linear history rule to be ignored when the bot is responsible for the PR (since exempted) and let the PR be marked as auto-merge.

Logs

Activity

  1. sergiou87 commented on May 19, 2026

    @sergiou87
    Contributor

    Thanks for your report! Since this seems to be covered by #13388, I'm gonna close it in favor of that one.

  2. marcusber commented on May 20, 2026

    @marcusber
    Author

    @sergiou87 Hi and thanks for your response! Reading through your suggested solution using --bypass-rules you say that it is mutually exclusive with --auto which is not going to work for me.
    I want to set it to auto (since I want CI to pass before merging) and merge using merge. Best of scenarios is that the rule is ignored, since the actor is exempted. I could of course send in something to bypass, but I will still need --auto.

  3. marcusber commented on May 20, 2026

    @marcusber
    Author

    Also, I don't want to override to pass the CI success rule. So this won't do it for this scenario if my assumptions are correct.

  4. sergiou87 commented on May 20, 2026

    @sergiou87
    Contributor

    @marcusber you're right — apologies for the hasty close, I'm reopening this.

    Let me gather some more context. Thank you for your patience 🙏

  5. babakks commented on May 20, 2026

    @babakks
    Member

    Hi @marcusber! 👋

    Let me build my mental model here.

    As a bit of context (look at #13388 (comment) for details), pr merge performs some client-side checks before submitting the merge mutation. That's because there's no way to tell the API to ignore/bypass rules. Basically, once a merge mutation is submitted the API assumes the user wants to ignore/bypass all they can. That's why, pr merge performs some checks to make sure the user is aware of any blocking rulesets or merge queues.

    With that context and looking at the error message you're getting:

    Run gh pr merge --merge --auto $PR_NUMBER
    GraphQL: Merge method merge commits are not allowed on this repository (enablePullRequestAutoMerge)
    Error: Process completed with exit code 1.
    

    I can see the merge mutation (i.e. API request) has been submitted. The GraphQL: prefix is actually the sign of an error returned from the API. So, regardless of any exempt being set or not, there's been no indication that the user (actually, the bot) did not have enough permissions to bypass rules in client side checks.


    If I set up the bot to allow bypass, the command is executed but since rules are not met it is not auto-merged. This could perhaps be solved by #13388.

    What rules are not met in this case? Maybe, what do you see in the web GUI on the PR merge box?

  6. marcusber commented on May 20, 2026

    @marcusber
    Author

    Ok @babakks and thanks for joining in!

    I changed the rule for the bot to Always allow

    Run gh pr merge --merge --auto $PR_NUMBER
      gh pr merge --merge --auto $PR_NUMBER
      shell: /usr/bin/bash -e {0}
      env:
    

    Successfully passed.

    Image

    Without the rule, this PR would have been merged already.

    When I click Disable auto-merge and change to merge:

    Image

    So basically, it did nothing, besides accepting the --auto and signed up for auto-merge but did not merge.

  7. niik commented on May 29, 2026

    @niik
    Contributor

    Hey @marcusber, as you noted in your original description, and as @babakks alluded to above, this likely isn't something gh itself can address.

    The GraphQL: prefix confirms this is a response from the API. gh submitted the mutation and the server rejected it. At this point there's nothing client-side we can change about how the server chooses to handle the request.

    If you'd like to inspect the exact request being made, you can run:

    GH_DEBUG=api gh pr merge --merge --auto $PR_NUMBER

    This will log the full GraphQL mutation, variables, and server response to stderr. If you spot anything in the request that we could be doing differently on our end, we'd love to hear about it!

    Otherwise, this may be worth raising with GitHub Support (https://support.github.com/) to clarify how ruleset actor exemptions are expected to interact with the enablePullRequestAutoMerge mutation.

    I'm going to close this issue as it doesn't appear to be actionable for us but please let me know if you think I'm in the wrong and we can reopen it

  8. marcusber commented on May 30, 2026

    @marcusber
    Author

    @niik Thanks for the response. Hearing you guys describing how it works, it sounds like a fair assumption that it has little to do with the gh-command itself.

  9. marcusber commented on Jun 5, 2026

    @marcusber
    Author

    @niik

    Heres the output, that I guess it confirms what you suspected:

    Run GH_DEBUG=api gh pr merge --merge --auto $PR_NUMBER
    [git remote -v]
    [git config --get-regexp ^remote\..*\.gh-resolved$]
    * Request at 2026-06-05 05:59:55.803749938 +0000 UTC m=+1.721077350
    * Request to https://api.github.com/graphql
    > POST /graphql HTTP/1.1
    > Host: api.github.com
    > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
    > Authorization: token xxxxx
    > Content-Length: 121
    > Content-Type: application/json
    > Graphql-Features: merge_queue
    > Time-Zone: Etc/UTC
    > User-Agent: GitHub CLI 2.92.0
    > X-Gh-Cache-Ttl: 24h0m0s
    > X-Github-Api-Version: 2022-11-28
    
    {
      "query": "query PullRequest_fields2{WorkflowRun: __type(name: \"WorkflowRun\"){fields(includeDeprecated: true){name}}}"
    }
    
    * Request at 2026-06-05 05:59:55.804216291 +0000 UTC m=+1.721543703
    * Request to https://api.github.com/graphql
    > POST /graphql HTTP/1.1
    > Host: api.github.com
    > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
    > Authorization: token xxxxx
    > Content-Length: 248
    > Content-Type: application/json
    > Graphql-Features: merge_queue
    > Time-Zone: Etc/UTC
    > User-Agent: GitHub CLI 2.92.0
    > X-Gh-Cache-Ttl: 24h0m0s
    > X-Github-Api-Version: 2022-11-28
    
    {
      "query": "query PullRequest_fields{PullRequest: __type(name: \"PullRequest\"){fields(includeDeprecated: true){name}},StatusCheckRollupContextConnection: __type(name: \"StatusCheckRollupContextConnection\"){fields(includeDeprecated: true){name}}}"
    }
    
    < HTTP/2.0 200 OK
    < Access-Control-Allow-Origin: *
    < Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning
    < Content-Security-Policy: default-src 'none'
    < Content-Type: application/json; charset=utf-8
            "number": repo_pr_id,
            "state": "OPEN",
            "title": "[Automated] Merge refs/heads/sourceBranch into targetBranch",
            "commits": {
              "nodes": [
                {
                  "commit": {
                    "oid": "commitId"
                  }
                }
              ]
            },
            "mergeStateStatus": "BLOCKED",
            "headRepositoryOwner": {
              "id": "organisationOid",
              "login": "organisationName"
            },
            "headRefName": "sourceBranch",
            "baseRefName": "targetBranch",
            "headRefOid": "commitId",
            "isInMergeQueue": false,
            "isMergeQueueEnabled": false
          }
        }
      }
    }
    
    * Request took 453.995295ms
    [git rev-parse --verify refs/heads/sourceBranch]
    * Request at 2026-06-05 05:59:56.718181983 +0000 UTC m=+2.635509395
    * Request to https://api.github.com/graphql
    > POST /graphql HTTP/1.1
    > Host: api.github.com
    > Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
    > Authorization: token xxxxx
    > Content-Length: 228
    > Content-Type: application/json
    > Graphql-Features: merge_queue
    > Time-Zone: Etc/UTC
    > User-Agent: GitHub CLI 2.92.0
    > X-Github-Api-Version: 2022-11-28
    
    GraphQL query:
    mutation PullRequestAutoMerge($input:EnablePullRequestAutoMergeInput!){enablePullRequestAutoMerge(input: $input){clientMutationId}}
    GraphQL variables: {"input":{"pullRequestId":"PR_pullrequestId","mergeMethod":"MERGE"}}
    
    < HTTP/2.0 200 OK
    < Access-Control-Allow-Origin: *
    < Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning
    < Content-Security-Policy: default-src 'none'
    < Content-Type: application/json; charset=utf-8
    < Date: Fri, 05 Jun 2026 05:59:57 GMT
    < Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
    < Server: github.com
    < Vary: Accept-Encoding, Accept, X-Requested-With
    < X-Content-Type-Options: nosniff
    < X-Frame-Options: deny
    < X-Github-Media-Type: github.v4; param=merge-info-preview.nebula-preview; format=json
    < X-Github-Request-Id: FC01:92182:2DC2D5D:AB4C87E:6A2265DC
    < X-Ratelimit-Limit: 12500
    < X-Ratelimit-Remaining: 12490
    < X-Ratelimit-Reset: 1780642778
    < X-Ratelimit-Resource: graphql
    < X-Ratelimit-Used: 10
    < X-Xss-Protection: 0
    
    {
      "data": {
        "enablePullRequestAutoMerge": null
      },
      "errors": [
        {
          "type": "UNPROCESSABLE",
          "path": [
            "enablePullRequestAutoMerge"
          ],
          "locations": [
            {
              "line": 1,
              "column": 72
            }
          ],
          "message": "Merge method merge commits are not allowed on this repository"
        }
      ]
    }
    
    * Request took 514.895112ms
    GraphQL: Merge method merge commits are not allowed on this repository (enablePullRequestAutoMerge)
    
  10. jgsuess commented on Jul 23, 2026

    @jgsuess

    +1, same root cause as #13388 for us. gh pr merge (including --auto) pre-checks mergeStateStatus client-side and bails out even for actors listed in a ruleset's bypass_actors. We confirmed the raw REST merge endpoint honors the bypass and merges instantly with no special flag — it's specifically the CLI's pre-flight gate (and the async auto-merge completion path) that ignores bypass eligibility. This blocks essential automation (Dependabot auto-merge) for us across several repos.

  11. jgsuess commented on Jul 23, 2026

    @jgsuess

    Follow-up: worth distinguishing two related-but-separate bugs here, since I've now also filed detailed repro on the platform side. This CLI issue is about gh pr merge's client-side mergeStateStatus pre-check refusing to attempt a merge for a bypass-eligible actor. Separately, there's a deeper platform bug where even GitHub's own async auto-merge completion (via enablePullRequestAutoMerge, not just gh) never re-checks bypass_actors when it finally executes the merge — confirmed via a clean 10-minute unattended test, with a direct synchronous PUT .../merge call succeeding instantly on the same PR. Details and repro steps posted at https://github.com/orgs/community/discussions/162623 and https://github.com/orgs/community/discussions/190610 in case that's useful context for whoever picks this up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    gh-prrelating to the gh pr command

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions