A team's pipeline waits for the reviewer bot's score before it merges a pull request. On October 28 it gave up on one PR with "no review for 25 min". The reviewer bot was fine. The tests were fine. The only thing wrong with the PR was that its branch conflicted with the base branch, and that one fact had switched off every workflow the PR would normally run.
Key facts
- When a PR's branch conflicts with its base, GitHub runs none of its
pull_requestworkflows: no tests, no reviewer bot, nothing. - Only
pull_request_targetworkflows still fire, and they run the base branch's code. - The checks on the PR page are from the last push that ran, so the PR looks stuck, not skipped.
- One command shows it:
gh pr view N --json mergeablereturnsCONFLICTING. - The fix is to rebase onto the base branch and push. The next push fires everything again.
What happened?
The repository is a monorepo with an AI reviewer bot and a set of required test jobs. Every push to a PR starts the tests and asks the bot for a review. A separate job waits for the bot's score and merges when it is good enough.
On this PR, the author pushed, then pushed again. Nothing new ran. No test run, no review, no comment. The PR page still showed green checks and an old review, but they belonged to an earlier commit, from before the base branch moved on and the conflict appeared.
The waiting job saw no new score and read the silence as "the reviewer is still working". It kept waiting until its time limit and then reported a review timeout. Someone looked at the reviewer bot first, because that is what the message pointed at. The bot had never been asked.
Which events fire when the branch conflicts?
Two kinds of workflow listen to pull requests. A push to a conflicting branch only reaches one of them.
GitHub's docs say it in one line, in the pull_request section: "Workflows will not run on pull_request activity if the pull request has a merge conflict. The merge conflict must be resolved first." A pull_request run tests the merge of the branch into its base, and with a conflict there is no merge to test. A pull_request_target run uses the base branch's code, so it does not need that merge and still fires.
How do you see it in one command?
Ask GitHub whether the PR can merge:
$ gh pr view 1234 --json mergeable
{"mergeable":"CONFLICTING"}
The answer is one of three values. MERGEABLE means the branch merges cleanly and workflows run as normal. CONFLICTING means nothing will run until the conflict is gone. UNKNOWN means GitHub is still working it out, usually right after a push; ask again a few seconds later.
The fix is plain: rebase the branch onto the base branch, resolve the conflict, and push. That push fires the usual pull_request event, and the tests and the reviewer bot run again as if nothing had happened.
Why don't required checks catch it?
Required status checks are meant to stop a bad merge. Here they stay quiet. With no run for the new head commit, its required checks are neither green nor red. They are blank, shown as expected or waiting. Nothing fails, so nothing alerts anyone.
GitHub will not merge a PR that still conflicts, so the conflict itself blocks the merge. The gap is in what the pipeline believes. It sees no failed check, only an empty slot. A pipeline that merges with gh pr merge --admin skips required checks, so nothing in its path ever says the head commit was never tested. Blank is not green, but an admin merge treats them the same.
What should a pipeline do?
- Check
mergeablebefore waiting. If it saysCONFLICTING, stop and report "conflicts with the base branch, rebase", not "waiting for review". The wait cannot end on its own. - Use a
pull_request_targetjob as the lane that always runs. It fires whenpull_requestdoes not, so a small job there can post "this PR conflicts with its base" as a comment. It runs with the base branch's code and secrets, so it must never check out or run the PR's code. Reading the PR's mergeable state and posting a comment is all it needs. - Treat "no run for the head commit" as its own state. It is not a pass and not a fail. A bot that only knows green and red will fold it into one of them, usually the wrong one.
- Give every wait a limit that reaches a person with the reason. "No review for 25 min" sent someone to the wrong place. "No workflows ran: the PR conflicts" would have taken one rebase.
A draft PR can be just as silent in some setups, which is the subject of a sibling post, A draft PR never gets reviewed; that one is about drafts skipping review, this one is about conflicts skipping every pull_request workflow.
Common questions
Does pull_request_target run on a conflicting PR?
Yes. It runs the base branch's code, so it does not need the PR to merge cleanly. Be careful with it: it has access to the repository's secrets, so it must not check out or run code from the PR.
Why does the PR page still show checks?
The checks you see are from the last push that ran, before the conflict. They describe an older commit, not the one at the head of the branch now.
Does rebasing fix it?
Yes, once the conflict is gone. The push after the rebase fires the pull_request event again, and every workflow runs on the new commit.
Melta is free; join the waitlist.