How to Find and Fix Stalled Pull Requests in GitHub
Stalled pull requests in GitHub quietly drain team momentum by costing authors context, letting branches drift from main, and blocking dependent work. This guide shows you how to define "stalled" with a measurable rule, find every stuck PR, diagnose why each one stopped, and put habits in place so new ones don't pile up.
A stalled pull request costs more than the days it sits open. The author loses context, the branch drifts from main, and the work the PR unblocks waits behind it. This guide gives you a repeatable way to find stalled pull requests in GitHub, work out why each one is stuck, and stop new ones from piling up. You need read access to your repos for searching (write access if you plan to comment, label, or close), a rough idea of your team's review expectations, and about 30 minutes for the first pass.
Step 1: Define what "stalled" means for your team
Start with a rule you can measure. "That PR feels old" produces arguments; "no commits, comments, or reviews for three business days on an open, non-draft PR" produces a list. GitHub has no built-in definition of stalled, so the threshold is your team's decision. Three business days is a reasonable starting point for a small startup team, but pick a number that matches how quickly your people normally review.
Separate stalled from slow. A large PR that gets a commit every day is in progress, even if it has been open for two weeks. A ten-line fix with no activity for four days is stuck. Measure time since last activity, not time since the PR was opened, and you avoid flagging healthy long-running work.
Then decide what stays out of the list:
- Drafts: Exclude them, since the author has signaled the work isn't ready for review. If a draft sits untouched for a few weeks, handle it with a separate, longer threshold.
- Blocked or on-hold PRs: Agree on a label such as blocked or on-hold and exclude it, so intentional pauses don't bury the real problems. Require a comment explaining what the PR is waiting on.
- Different PR types: A dependency bump and a database migration rarely deserve the same threshold. If one number clearly doesn't fit, note an exception rather than inflating the rule for everyone.
Finally, write the definition down in your contributing guide or team wiki. A rule that lives in one manager's head will be applied inconsistently, and people will feel singled out when their PR gets flagged.
Step 2: Pull a list of stale open PRs with GitHub search
GitHub's search qualifiers can build your first list in a couple of minutes. Open the Pull requests tab of a repo (or go to github.com/pulls for your own PRs), and enter a query in the search bar. Start with this:
is:pr is:open draft:false updated:<2026-09-24
That returns open, non-draft PRs with no updates since the date you specify. Calculate the date from your threshold: count back your chosen number of business days from today and use that date. If your threshold is three business days and today is Thursday, October 1, 2026, the cutoff is Monday, September 28. Note that updated reflects any activity on the PR, including comments and pushes, which is what you want.
Scope and sort
Narrow the search to where you need it:
- org:your-org searches every repo in the organization.
- repo:owner/name limits results to one repository.
- sort:updated-asc puts the least recently updated PRs first. In the Pull requests tab you can also pick "Least recently updated" from the Sort menu.
Split by review state
Add a review qualifier to separate the causes. review:none finds PRs nobody has reviewed. review:required finds PRs still waiting on a required review. review:changes_requested finds PRs where a reviewer asked for changes, so the ball is likely with the author. Running each as its own query gives you a head start on the diagnosis in Step 3.
Excluding your blocked label is one more qualifier: -label:blocked (use whatever name you chose in Step 1).
Save each query as a browser bookmark or pin the link in your team channel. One caution: qualifier syntax and available filters change from time to time, so check the query against GitHub's current documentation on searching issues and pull requests if a result looks wrong. A date-based query also needs its date updated each time you run it.
Step 3: Diagnose why each PR is stuck
An age-sorted list tells you what is old, not why. Open each stalled PR and sort it into one cause. Most fall into five buckets:
- Waiting on a first review
- Waiting on the author to address feedback
- Failing CI
- Merge conflicts with the base branch
- Unclear scope or priority
The fastest signal is the last actor in the timeline. Scroll to the bottom of the conversation. If the author acted last, with a push or a reply, the ball is with the reviewers. If a reviewer acted last by requesting changes or asking a question, the ball is with the author. Knowing whose move it is tells you whom to talk to.
Check the checks next. A red CI status often explains silence, because reviewers tend to skip PRs that don't pass. Look at whether the failure is real or a flaky test that fails on rerun, since those need different fixes.
Then look at size. On the Files changed tab, note the lines changed and the number of files touched. Very large diffs are a common reason reviewers put a PR off: nobody wants to start a two-hour review at 4 p.m. If a PR has no review after several days and touches dozens of files, size is the likely culprit, not neglect.
Finally, check the linked issue. If it lives in Linear or another tracker, confirm the work is still a priority. Some PRs stall because the team quietly moved on, and no amount of nudging will help. Record your finding in a simple list: PR, cause, owner of the next move. You'll use it in the next step.
Step 4: Unblock each PR with the right action
Match the fix to the cause you recorded, rather than leaving the same generic nudge everywhere:
- Waiting on first review: Assign or re-request a specific reviewer. "Anyone free to look?" spreads responsibility until nobody takes it. Choose the person with the most context on that code and ask directly.
- Waiting on the author: Ask the author what they need. Often the feedback was ambiguous or the author was pulled onto something else. If they are overloaded, consider whether someone else should take over the branch.
- Failing CI: Rerun the job to confirm whether the failure is flaky. If it is, fix or quarantine the test so it stops blocking unrelated work. If it is real, tell the author which check to look at.
- Merge conflicts: Have the author rebase or merge the base branch and resolve conflicts, then re-request review, since reviewers may not notice the update.
- Oversized diff: Propose splitting it into smaller PRs, for example a refactor first and the behavior change second. It takes effort up front, but small PRs get reviewed faster.
Whatever you do, leave a comment that names the next action and its owner. Compare "bump" with "@maria, this needs your review on the retry logic in queue.ts; @dev, please rebase once she approves." The second tells everyone what happens next and who is responsible. It also leaves a record you can scan later.
Close PRs that are obsolete. If the work was deprioritized or replaced, close the PR with a short note explaining why and link to the replacement if there is one. Closing is a legitimate outcome, and a clean list is easier to manage than one full of dead branches.
Escalate to the tech lead when a PR is blocked by a cross-team dependency or a disagreement over approach. Reviewers and authors should not have to settle architectural disputes through comment threads that stretch for a week. A quick decision from someone with authority unblocks the PR and the people waiting on it.
Step 5: Automate reminders and labeling
Manual sweeps work, but they depend on someone remembering. Automation covers the routine parts so people can focus on the judgment calls.
Label inactive PRs with a stale action
The actions/stale GitHub Action runs on a schedule and labels PRs with no recent activity, and it can optionally close them after a grace period. The main settings to configure are the number of days before a PR is marked stale (days-before-pr-stale), the days before closing (days-before-pr-close), the label applied, and the labels that exempt a PR (exempt-pr-labels). Put your blocked and on-hold labels in the exempt list. Check the action's repository for the current version and input names before you copy a configuration, since they change between releases.
Be conservative about closing. Setting days-before-pr-close to a negative value turns off auto-closing, which is a sensible default while you learn how your team's PRs behave. Auto-closing too aggressively punishes slow but legitimate work and annoys contributors.
Route reviews automatically
Add a CODEOWNERS file to the repo so GitHub automatically requests review from the right people based on which files a PR changes. Pair it with team review-request settings so reviews are distributed rather than always landing on the same person. Many "waiting on first review" stalls disappear when a reviewer is assigned at creation.
Send a digest
Post a scheduled list of stale PRs to Slack. GitHub offers scheduled reminders for teams, which send pull request summaries to Slack, though availability and setup can change, so confirm it in your organization's settings. If it doesn't fit, a small scheduled workflow that runs your search and posts the results does the same job.
The main risk is noise. Start with a generous threshold, exempt drafts and blocked PRs, and send one digest rather than many pings. If people learn to ignore the bot, you've made the problem worse.
Step 6: Set team habits that prevent stalls
Tools surface stalls, but habits stop them from forming. Four practices matter most.
Agree on a review response target. A common choice is a first review within one business day. It doesn't have to mean a full approval, just an acknowledgment and initial feedback. Bring it up in standup or weekly planning so it stays visible, and adjust it if the team consistently misses it, since an unrealistic target gets ignored.
Keep PRs small and well described. One change per PR is easier to review and easier to revert. Ask authors to add context in the description: what changed, why, and how it was tested. A reviewer who understands the intent in 30 seconds is far more likely to start the review now rather than later.
Run a short weekly sweep. Fifteen minutes with your saved search is enough. Go through the list, confirm the next action and owner for each PR, and close what's obsolete. The point is that nothing ages quietly for more than a week.
Track trends, not individuals. Count stalled PRs each week and watch time to first review, meaning the gap between a PR being opened and its first review. If either climbs, look for a systemic cause such as too few reviewers, oversized work, or competing priorities. Using these numbers to blame a person damages trust and tends to make people hide problems instead of raising them.
Step 7: Monitor stalls continuously with Progress
Saved searches and stale bots only catch what you remember to check, and they treat every PR as an isolated item. Progress takes a different approach. It ingests activity from GitHub and Linear and flags stalled work as a pre-computed signal, so the assessment is already done when you open it rather than something you assemble from search results.
The useful step beyond a PR list is connecting stalls to the work they belong to. Progress tracks initiative and work-stream health, so you can see that three stuck PRs sit under the same initiative and that the initiative is at risk, not only that some PRs are old. That changes the conversation from "who should review this?" to "is this stream of work still on track?"
You can also ask questions in plain language through the MCP server and Claude integration. For example, ask which PRs have stalled this week and on which initiatives, and get an answer grounded in actual activity data instead of building a query by hand.
Repeated stalls are sometimes a people problem, not a process one. Progress's team momentum and morale reads help you notice when work is slowing across a team, or when engagement is dropping, so you can tell overload or disengagement apart from a missing review rule. Fixing the process won't help if the underlying issue is a team stretched too thin.
Running the second pass and tuning from there
Run your saved search again after a week and compare the stalled count to your first pass. If it dropped, note which fixes did the most work. If it didn't, check which cause bucket from Step 3 dominates and focus there.
Then adjust. A threshold that flags too many PRs needs loosening, and one that flags almost none may be too generous. Revisit your stale-bot settings and digest frequency the same way. If you'd rather have the signals delivered without maintaining the searches yourself, Learn more about our services.