gh-pr-merge-unstable-state-needs-auto-and-watch-branch-deletes

star 2

Diagnose and recover from two adjacent `gh pr merge` failure modes that masquerade as merge conflicts. Use when: (1) `gh pr merge --squash` (or `--merge`) errors with "To have the pull request merged after all the requirements have been met, add the `--auto` flag" AND "Run the following to resolve the merge conflicts locally" even though you just pushed a clean resolution; (2) `gh pr view <N> --json mergeable,mergeStateStatus` returns `MERGEABLE` + `UNSTABLE` rather than `MERGEABLE` + `CLEAN`; (3) you delete the remote branch (`git push origin --delete <branch>`) immediately after `gh pr merge` returns a conflict warning thinking the merge succeeded — and discover the PR has flipped to `CLOSED` rather than `MERGED`. The actual root cause for (1)/(2) is almost always pending CI / branch-protection checks, NOT real merge conflicts; the fix is `--auto` flag so `gh` queues the merge for when checks pass. For (3) — recovery requires restoring the remote branch (`git push -u origin <branch>` from your local copy) a

wan-huiyan By wan-huiyan schedule Updated 5/29/2026

name: gh-pr-merge-unstable-state-needs-auto-and-watch-branch-deletes description: | Diagnose and recover from two adjacent gh pr merge failure modes that masquerade as merge conflicts. Use when: (1) gh pr merge --squash (or --merge) errors with "To have the pull request merged after all the requirements have been met, add the --auto flag" AND "Run the following to resolve the merge conflicts locally" even though you just pushed a clean resolution; (2) gh pr view <N> --json mergeable,mergeStateStatus returns MERGEABLE + UNSTABLE rather than MERGEABLE + CLEAN; (3) you delete the remote branch (git push origin --delete <branch>) immediately after gh pr merge returns a conflict warning thinking the merge succeeded — and discover the PR has flipped to CLOSED rather than MERGED. The actual root cause for (1)/(2) is almost always pending CI / branch-protection checks, NOT real merge conflicts; the fix is --auto flag so gh queues the merge for when checks pass. For (3) — recovery requires restoring the remote branch (git push -u origin <branch> from your local copy) and gh pr reopen <N> before retrying. Sibling to gh-pr-merge-worktree-checkout-trap (different failure mode — worktree holding main). author: Claude Code version: 1.0.0 date: 2026-05-19

gh pr merge UNSTABLE state + branch-delete recovery

Problem

Two adjacent failure modes that read as "merge conflicts" but aren't:

Failure A: gh pr merge rejects with "conflicts" when there are none

You push a clean resolution of merge conflicts, GitHub shows the PR as MERGEABLE, and yet:

$ gh pr merge 915 --squash
To have the pull request merged after all the requirements have been met,
add the `--auto` flag.
Run the following to resolve the merge conflicts locally:
  gh pr checkout 915 && git fetch origin main && git merge origin/main

The first line is the actual hint; the second is misleading boilerplate. The real cause is mergeStateStatus: UNSTABLE — branch protection or CI checks are pending. gh can't merge yet because the repo's rules aren't satisfied, not because of file-level conflicts.

Failure B: PR auto-closes if you delete the remote branch right after Failure A

Common pattern: previous PR on the same branch merged cleanly with gh pr merge <N> --squash. You then do git push origin --delete <branch> to clean up. Habit kicks in for the next PR — you run gh pr merge (gets Failure A above), notice the "conflicts" warning, run git push origin --delete <branch> anyway thinking the merge already landed remotely...

$ gh pr view 915 --json state
{"state":"CLOSED"}   # ← not MERGED — the PR closed because the branch was deleted with the merge un-queued

GitHub treats a deleted-branch PR with no merge in progress as abandoned and closes it.

Context / Trigger conditions

  • gh pr merge --squash (or --merge) returns with the "add the --auto flag" / "resolve conflicts" stderr
  • gh pr view <N> --json state,mergeable,mergeStateStatus shows {state: OPEN, mergeable: MERGEABLE, mergeStateStatus: UNSTABLE} — the UNSTABLE is diagnostic; it means "no real conflicts, but checks aren't passing yet"
  • Or: a PR you just tried to merge is now state: CLOSED rather than state: MERGED, AND the remote branch is missing
  • Repo has branch protection rules (required status checks, required reviews) OR CI workflows that haven't completed yet
  • You're in a multi-PR session where the previous PR merged cleanly, conditioning you to expect the same flow

Root cause

For Failure A: gh pr merge (without --auto) requires the PR to be fully ready to merge RIGHT NOW. If branch-protection rules or CI checks are pending, gh reports it as a generic merge problem with misleading conflict-resolution advice. The actual diagnostic is the mergeStateStatus field — UNSTABLE means "pending checks", BLOCKED means "failed checks or missing reviews", CLEAN means "ready to merge", DIRTY means "real file conflicts".

For Failure B: GitHub's UI treats "branch deleted + no merge in progress" as PR abandonment. The PR closes; the merge never happens; recovery requires restoring the branch AND reopening the PR.

Solution

Diagnose first — three-line state check

Before retrying or panicking:

gh pr view <N> --json state,mergeable,mergeStateStatus,statusCheckRollup

Interpretation:

state mergeable mergeStateStatus Meaning Action
OPEN MERGEABLE CLEAN Ready to merge gh pr merge <N> --squash
OPEN MERGEABLE UNSTABLE Pending CI / checks gh pr merge <N> --squash --auto
OPEN MERGEABLE BLOCKED Failed checks or missing required reviews Fix the failed check or request review
OPEN CONFLICTING DIRTY Real file conflicts Resolve locally, push, retry
CLOSED (any) (any) PR closed (often: branch deleted) Restore branch + reopen (see recovery below)
MERGED (any) (any) Already done Nothing to do

Fix Failure A — use --auto

gh pr merge <N> --squash --auto
# Returns silently; merge queues for when checks pass; GitHub auto-deletes remote branch on merge

--auto tells GitHub to wait for branch-protection requirements and then merge automatically. No polling required — GitHub handles it. The remote branch is deleted by GitHub at merge time (no need to do it yourself).

Fix Failure B — restore branch + reopen PR + retry

# Your local branch still has the commits — push it back up
git push -u origin <branch-name>

# Reopen the PR (gh closed it when the branch went away)
gh pr reopen <N>

# Verify it's back
gh pr view <N> --json state,mergeable,mergeStateStatus

# Then merge with --auto
gh pr merge <N> --squash --auto

If you've already discarded the local branch (e.g., git branch -D after the wrong-time delete), recovery is harder — you may need to recover commits via git reflog or recreate from a colleague's clone.

Verification

After applying --auto:

  1. gh pr view <N> --json state should still show OPEN immediately after the command
  2. Within minutes (or however long your CI takes), GitHub will merge automatically
  3. Final state: gh pr view <N> --json state,mergedAt shows {state: MERGED, mergedAt: <timestamp>}
  4. Remote branch is auto-deleted by GitHub
  5. You receive no email failure notification (a clean signal)

Example

Session ending PR #915 (a docs handoff), 2026-05-19:

$ gh pr merge 915 --squash
To have the pull request merged after all the requirements have been met, add the `--auto` flag.
Run the following to resolve the merge conflicts locally:
  gh pr checkout 915 && git fetch origin main && git merge origin/main

$ gh pr view 915 --json state,mergeable,mergeStateStatus
{"mergeable":"MERGEABLE","mergeStateStatus":"UNSTABLE","state":"OPEN"}
                                  ^^^^^^^^
                                  not DIRTY → no real conflicts; pending checks

$ gh pr merge 915 --squash --auto
# (silent success — queues for CI)

(Earlier in the same session, the same orchestrator hit Failure B on the same PR by running git push origin --delete docs/s207-handoff immediately after gh pr merge returned the conflict warning — the PR flipped to CLOSED. Recovered with git push -u origin docs/s207-handoff + gh pr reopen 915 + gh pr merge 915 --squash --auto.)

Notes

  • Don't reflexively delete the remote branch after gh pr merge errors. The merge may not have landed. Check gh pr view <N> --json state,mergedAt first; only delete if mergedAt is non-null.
  • --auto is safe to use even when checks pass immediately. If the PR is already CLEAN, --auto merges instantly. No downside to using it as the default.
  • Branch-protection diagnostics: if mergeStateStatus stays UNSTABLE for >5min after a push, check gh pr checks <N> to see which check is hanging. A common cause is a paths-filter workflow that didn't trigger because the PR touched no matching paths — that check stays "pending" forever and blocks merge until the rule is updated.
  • Sibling failure mode: gh-pr-merge-worktree-checkout-trap — when gh pr merge --delete-branch fails with fatal: 'main' is already used by worktree at .... That's a LOCAL-side failure of the post-merge branch checkout; the GitHub merge itself succeeds. Different beast from the failures documented here — verify by checking gh pr view <N> --json mergedAt.
  • Required-status-check repos: in repos with branch protection requiring specific status checks, --auto is essentially mandatory — no human can merge instantly if checks aren't done. Build the habit project-wide.

References

Install via CLI
npx skills add https://github.com/wan-huiyan/agent-traffic-control --skill gh-pr-merge-unstable-state-needs-auto-and-watch-branch-deletes
Repository Details
star Stars 2
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator