A silent merge queue bug corrupted 658 GitHub repos
GitHub's merge queue is supposed to protect the default branch. An incomplete feature-flag gate let an unreleased merge-base path run inside squash merge groups, producing valid-looking commits that silently reverted prior work while availability monitoring stayed green.
The merge queue's job is to protect the last dangerous step before code reaches the default branch. It takes pull requests that have passed review and CI, groups them, retests the combined state, and lands them in order. That contract depends on one quiet guarantee: the commit the queue writes must contain exactly what was validated. If the queue produces a commit that looks valid but has the wrong tree, the protection mechanism becomes the thing corrupting main.
On April 23, 2026, GitHub deployed a change to its Pull Requests service. The change introduced a new code path for merge-base computation during merge queue ref updates. It was supposed to stay dormant behind a feature flag for an unreleased feature, but the gate was incomplete. In the specific case of merge queue groups using the squash method with more than one pull request, the new path ran anyway.
That mattered because squash merge groups depend on choosing the right base for the final three-way merge. With the wrong base, GitHub could create a commit that appeared to advance the default branch while quietly dropping changes that had already been merged into the group. The Git history still existed. The commit object was structurally valid. The damage was semantic: the branch tip no longer represented the code the queue was supposed to land.
For 3.5 hours, the failure stayed invisible to GitHub's automated monitoring. Request rates, latencies, and errors did not describe the problem, because the service was still operating. It was just writing the wrong result for one configuration: multi-PR merge queue groups using squash. Single-PR groups, ordinary merges, rebases, and pull requests merged outside merge queue were not affected.
The first useful signal came from customers. Users noticed that pull requests showed as merged even though the expected changes were missing from HEAD, and some saw restored commits appear while the public status page looked healthy. GitHub became aware at 19:38 UTC after an increase in support inquiries, then traced the issue back to the incomplete feature-flag gate.
GitHub reverted the code change and force-deployed the fix by 20:43 UTC. The final impact was 658 repositories and 2,092 pull requests. No commits were lost, but affected default branches were wrong, and GitHub could not safely repair every repository automatically. After resolution, it identified affected repositories and sent targeted remediation instructions to repository administrators.
The lasting failure mode is the partial gate. A feature flag can create a false sense of containment when it guards the common paths but misses one variant combination. Here, the missed combination was exactly where correctness mattered most: the automated system trusted to serialize merges silently produced commits that undid earlier work, while the availability dashboard had no reason to turn red.
Automated checks did not validate merge correctness for multi-PR squash groups.// GitHub, Merge Queue Availability, April 2026
From the first signal to all-clear in 4h38m.
An unreleased code path escaped its feature flag.
The merge queue service received a new code path that changed how merge base was computed for merge queue ref updates. The path was written to be gated behind a feature flag for an unreleased feature. The gating logic was incomplete: in the specific case of squash merge groups, the new computation ran unconditionally regardless of the flag's state.
When a merge queue group contained multiple pull requests using the squash merge method, the incorrect merge base caused a faulty three-way merge. The result was a commit that appeared to advance the target branch but reverted changes from pull requests that had already been merged into the group. The commit landed in the repository's history looking structurally valid — the corruption was only visible when comparing the resulting tree against what the branch should have contained.
Automated monitoring tracked availability signals: request rates, latencies, error counts. It had no signal for whether the contents of a merge commit were correct. The bug was silent from monitoring's perspective for the entire window between deployment and the first customer report.