Your pull request is ready — two approvals, all CI checks green — but the Merge button on GitHub is still grayed out with the message “This branch cannot be merged until all required reviews are approved” or “Required status check ‘build / test’ is expected.” Or on GitLab, the “Merge” button is disabled with “This merge request is blocked by 1 unresolved thread.” The protection rules are working as intended, but something in the PR pipeline is misconfigured, stale, or the rule itself was recently tightened. This guide walks through each common blocker and how to unblock the merge without permanently weakening the protection.
Common causes
Ordered by hit rate, highest first.
1. Required status check name changed in CI but not in the protection rule
The GitHub Actions workflow was renamed from CI / test to ci / run-tests, but the branch protection rule still requires CI / test. The new job name never satisfies the old requirement.
How to spot it: Go to Settings > Branches > protection rule > “Require status checks to pass.” Compare the listed check names with the job names in .github/workflows/*.yml. A mismatch is the culprit.
2. Review was approved before the last commit, and “dismiss stale approvals” is enabled
A reviewer approved the PR at commit abc123. Then a small typo fix was committed as def456. With “dismiss stale pull request approvals when new commits are pushed” enabled, the approval was automatically revoked.
How to spot it: The “Changes requested / approved” badge on the PR disappeared after the last commit, even though no reviewer changed their vote.
3. The “required reviews” count was recently increased
An admin raised the required approval count from 1 to 2 after the PR already had 1 approval. Old PRs that were previously one approval away from merging now need another reviewer.
How to spot it: Check the protection rule’s “Require a pull request before merging” setting — compare the current count with the number of existing approvals.
4. Codeowners rule requires review from a specific team that has not reviewed
A CODEOWNERS entry like src/billing/** @org/billing-team means PRs touching billing code require at least one approval from someone in billing-team, regardless of how many other reviews exist.
How to spot it: The PR touches paths covered by CODEOWNERS. The “Review required from CODEOWNERS” badge is yellow, and no member of the owning team has approved.
5. A required CI job never ran because of path filtering
The workflow has paths: filtering (e.g., only runs on changes to src/**). A PR that only changes docs/** never triggers the workflow, so the required check never appears as “passing” — it appears as “expected” (pending).
How to spot it: The CI job shows as “Expected” on the PR checks list rather than “Passed” or “Failed.” The diff only touches excluded paths.
6. Merge is blocked by an unresolved thread on GitLab
GitLab’s “All threads must be resolved before merge” setting blocks the merge if even one comment thread is marked unresolved, including resolved conversations that were re-opened.
How to spot it: Look at the “Discussions” tab — the unresolved thread count is shown in the merge readiness checklist.
Shortest path to fix
Step 1: Read the exact blocking message from the merge button
On GitHub: Expand the "Why can't this PR be merged?" section under the merge button.
On GitLab: Scroll to the merge widget — each blocker is listed with a red X.
On Bitbucket: Check the "Merge Checklist" panel on the right side of the PR.
Each platform identifies the specific rule that is blocking — start with the first red item.
Step 2: Fix a stale required-check name
# In GitHub: Settings > Branches > Edit rule > Required status checks
# Type the exact new job name as it appears in the Actions tab
# Save and re-run the failing check:
gh workflow run ci.yml --ref my-branch
Step 3: Request a re-review after a post-approval commit
Tag the reviewer in a comment explaining the change was minor, or push an empty commit to reset the check state:
# Empty commit to re-trigger CI (does NOT re-request review, but shows intent)
git commit --allow-empty -m "ci: trigger re-check after typo fix"
git push origin my-branch
For the review itself, use GitHub’s “Re-request review” button (the circular arrow icon next to the reviewer’s name).
Step 4: Fix a path-filtered CI job that never ran
# Temporarily broaden the paths filter in the workflow file, or
# push an empty touch of a file in the monitored path:
touch src/.keep
git add src/.keep
git commit -m "ci: trigger required check for path-filtered workflow"
git push origin my-branch
After merge, remove the .keep file in a follow-up commit.
Step 5: Resolve GitLab unresolved threads
Go to each thread marked unresolved, click “Resolve thread.” If a thread is genuinely addressed, the author or any maintainer can resolve it. Re-check the merge widget after all threads are resolved.
Step 6: Admin override as a last resort
If the merge is genuinely time-critical and the blocker cannot be resolved (e.g., the required reviewer is on leave):
# On GitHub: Admin "Merge without waiting for requirements" option
# Only available to repository admins, logged and auditable
Document the override reason in the PR description.
Prevention
- Keep required status check names in sync with workflow job names — use exact string matching and update both in the same PR when renaming CI jobs.
- Use a
CODEOWNERStriage process: ensure every owned path has at least two team members who can review to avoid single-reviewer bottlenecks. - When adding new branch protection rules, audit open PRs first to ensure none are suddenly blocked without warning.
- Set “dismiss stale approvals” only for critical branches (main, release) — on feature branches it adds friction without meaningful security benefit.
- Configure path-aware required checks carefully: if a job should be “required” only when certain paths change, use GitHub’s “required if paths match” option (available in rulesets).
- Document your branch protection rationale in a
CONTRIBUTING.mdso teammates understand why checks exist and can diagnose blocks themselves. - Test protection rule changes in a sandbox repository before applying them to production repos.
FAQ
Q: I’m an admin and I still can’t merge. How is that possible? A: “Include administrators” in the protection rule removes the admin bypass. If this option is enabled, admins are subject to the same rules as everyone else. To merge, you must satisfy the rules or temporarily disable “Include administrators.”
Q: The CI check passed but GitHub still shows it as pending. Why? A: The check may be from a different run (different branch or SHA). Ensure the check that passed is on the same commit SHA as the PR’s HEAD. If the PR was updated after the check ran, re-run CI on the latest commit.
Q: Our required reviewer is out of office for two weeks. Can we add a temporary co-owner?
A: Yes. On GitHub, add the co-owner to the CODEOWNERS file in a PR, merge it (using admin override if necessary), then the co-owner can approve your blocked PR.
Q: How do I audit who changed the branch protection rule that broke our workflow?
A: On GitHub, go to the repo’s Audit Log (Settings > Audit Log > filter by protected_branch). GitLab and Bitbucket have equivalent audit log sections.