Skip to content

--delete-branch fails, if branch is deleted already (race condition) #11187

Description

@V0idC0de

Describe the bug

When using gh pr merge --delete-branch, the command sometimes fails, if the branch was deleted already. This is unintuitive, since I want to make sure the branch is cleaned up and don't care how it happens, unless it interferes with the actual command.

#1279 already addresses this behavior, but I'm still getting an error.
I wondered, whether this is intentional or not. If intentional, where is the difference to the behavior addressed in the aforementioned PR and how I should go about ensuring the branch is gone after the merge, without risking a pipeline fail.

Affected version

GitHub CLI 2.74.2 (pipeline ran today and was using the Ubuntu 24.04 runner image, thus this is the installed version)

Steps to reproduce the behavior

  1. Write pipeline which merges a PR
  2. Set the repository to delete the branch itself (or insert a sleep into the pipeline and do it yourself)
  3. Let the pipeline run the "gh pr merge --delete-branch" command on that PR
  4. gh will error with a HTTP 404

Expected vs actual behavior

I'd expect the command to not error, but gracefully handle the error and suppressing it, since it matches the intent.
Currently it fails with failed to delete remote branch <redacted>: HTTP 404: Reference does not exist (https://api.github.com/repos/<redacted>).
#1279 already addresses this issue, but the caught error doesn't include the HTTP 404 I'm observing here.

I'd argue, that the command should not raise this error, since my intention is "branch should be cleaned up and gone after merge". If the branch is already gone, that's fine, as far as I'm concerned. The fact, that the branch was deleted by some other actor has no impact on my gh pr merge command, since the merge already happened, thus the command shouldn't care and shouldn't fail, just because it could not delete the branch by itself.

Logs

There is only one relevant log line, as gh pr merge doesn't output anything else for me.

failed to delete remote branch <redacted>: HTTP 404: Reference does not exist (https://api.github.com/repos/<redacted>)

Activity

  1. babakks commented on Jul 4, 2025

    @babakks
    Member

    Thanks for reporting this, @V0idC0de! 🎉

    I couldn't reproduce this, perhaps due to the race-condition nature. However, I checked with the platform, and it seems the error message Reference does not exist can be returned along with both 404 or 422 status codes. The underlying reason is different, but it doesn't make any difference for our use case. Also, it seems this situation (i.e. a 404 response) is rare.

    Note

    I looked for similar issues in other projects and found a few cases.

    So, I can confirm what you're seeing is an uncovered API behaviour in gh. I'm going to put down the A/C for this change in the next comment.

    Further investigation

    The interesting part is, HTTP 422 can be returned in a couple of situations, one of which is when the ref is not found. So, I think the current implementation is not quite correct, since it's just based on a mere status code check:

    if !m.merged {
    apiClient := api.NewClientFromHTTP(m.httpClient)
    err := api.BranchDeleteRemote(apiClient, m.baseRepo, m.pr.HeadRefName)
    var httpErr api.HTTPError
    // The ref might have already been deleted by GitHub
    if err != nil && (!errors.As(err, &httpErr) || httpErr.StatusCode != 422) {
    return fmt.Errorf("failed to delete remote branch %s: %w", m.cs.Cyan(m.pr.HeadRefName), err)
    }
    }

    As with our API errors, detecting the cause of an error (without checking the error message) is not possible. For example, this is the error response we get when an API call to delete a ref fails:

    {
      "message": "Reference does not exist",
      "documentation_url": "https://docs.github.com/rest/git/refs#delete-a-reference",
      "status": "422"
    }

    I'm not sure if we're going to introduce another error message check, though.

  2. babakks commented on Jul 4, 2025

    @babakks
    Member

    The A/C here is a bit awkward since the changes is almost invisible from the user's side. However, an A/C feels more appropriate than an E/O.

    For now I assumed we're not going to check the error messages. The reasons are:

    1. Checking error messages is not a future-proof approach.
    2. An HTTP 404 error is only raised when the ref is not found, so there's no need to check for the message. What has been already done for HTTP 422 remains the same and since we haven't received issues or bug reports for it, we can assume it's been working fine.

    Acceptance Criteria

    Given I have a PR ready to be merged AND the branch is already deleted (delete ref API returns 404)
    When I run gh pr merge --delete-branch
    Then gh should exit without an error (zero exit-code)

  3. added
    gh-prrelating to the gh pr command
    priority-3Affects a small number of users or is largely cosmetic
    and removed on Jul 4, 2025
  4. self-assigned this
    on Jul 8, 2025
  5. babakks commented on Jul 10, 2025

    @babakks
    Member

    @V0idC0de, This is now fixed in our latest release v2.75.0. However, it takes a couple of weeks for GitHub runners to upgrade their installation. If that's a blocker, you might want to manually upgrade gh in your workflow.

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinggh-prrelating to the gh pr commandpriority-3Affects a small number of users or is largely cosmetic

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions