[BUG] Footer PR badge silently and permanently disabled for the rest of the session after a single slow (>4s) status fetch — no re-enable path, while the gh spawn timeout is 5s
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet (closest are anthropics/claude-code#67055 — gh timeout misclassified as expired auth in the Desktop toast, a different code path — and the stale-badge family like anthropics/claude-code#63767, which is the opposite direction: a badge that should disappear but stays)
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
The footer "PR review status" badge (documented in interactive-mode docs) can disappear
mid-session and never come back for the remainder of that session, even though:
- the current branch has an open, approved PR,
ghis installed, authenticated, andgh pr viewsucceeds instantly when run manually
in the same session (! prefix),
- the docs say the status refreshes every 60 seconds and immediately after a
gh pr/
git push command runs in the session.
The custom-statusline mirror fields (pr.number / pr.url / pr.review_state in the
statusline stdin JSON) are also absent, while workspace.repo andworkspace.git_worktree are correctly populated — so repo/worktree detection is fine and
it is specifically the PR status poller that has stopped.
A brand-new session started in the same worktree directory shows the badge (and the
statusline pr.* fields) within seconds. So the breakage is per-session-process state,
not environmental.
Root cause (from inspecting the bundled JS, v2.1.217 binary)
The PR status poller has a fetch-duration kill switch. After each fetch it checks the
elapsed time and, if a single fetch took longer than 4 seconds, permanently disables the
poller:
// constants: kiS = 4000 (fetch-duration limit), gh spawn timeout = 5000
let P = Date.now() - S;
if (P > kiS) { if (ebe.disabled = !0, nzr()) $e(...); return }
Problems with this branch:
- There is no re-enable path.
ebe = {disabled:!1, badStreak:0}is a module-level
object and the only assignments to ebe.disabled in the whole bundle set it to true
(this 4s branch, plus a 3-consecutive-failures branch). Nothing ever resets it. Once
tripped, the 60-second poll loop and the documented immediate refresh after gh pr /
git push are both dead for the rest of the process lifetime.
- The limit is below the subprocess timeout. The
ghinvocation itself is spawned
with a 5000 ms timeout, so any fetch in the 4–5 s window "succeeds" yet still
permanently kills the feature.
- It is completely silent in the gh-CLI mode. The telemetry call in that branch is
gated on the alternative (direct-API) implementation flag; in the default gh mode
there is no log line, no telemetry, no UI hint. --debug logs show nothing either.
From the user's perspective the badge just never comes back.
One aggravating interaction: the poller short-circuits (no fetch at all) while the
current branch equals the default branch. A session started at the repo root therefore
performs its first real fetch only after it moves onto a PR branch (e.g. viaEnterWorktree) — typically mid-session, under load. A single cold gh pr view taking
more than 4 s at that point kills the badge for the whole session. This makes
long-running sessions disproportionately likely to lose the badge — the longer the
session, the more chances for one slow fetch — which matches my field observation: a
long-running (multi-hour) --worktree session had the badge permanently missing, while
a fresh session in the same worktree showed it instantly.
What Should Happen?
A transiently slow fetch should degrade gracefully, not permanently disable the feature.
Any of the following would resolve it (in preference order):
- Re-enable the poller after a backoff instead of disabling it forever (treat the >4s
case like the existing miss/backoff handling).
- At minimum, clear the disabled flag on the documented explicit triggers (a
gh pror
git push command running in the session), so the state is user-recoverable.
- Emit a debug-log line (and telemetry in both modes) when the poller disables itself,
so this state is diagnosable without binary spelunking.
Steps to Reproduce
Timing-dependent, but reliably reproducible by making gh slow once:
- Put a
ghshim on PATH that delays onepr viewcall by ~4.5 s (under the 5 s spawn
timeout), then behaves normally.
- Start
claudeon a branch with an open PR. The first poll trips the 4 s check. - Observe: the badge never appears for the rest of the session; running
gh pr viewin
the session does not bring it back; statusline JSON pr stays absent. Restarting the
session restores the badge (until the next slow fetch).
Diagnosed by mirroring pr.*, workspace.repo, workspace.git_worktree, and cwd into
a custom statusline and comparing a broken long-running session against a fresh session
in the same worktree, then locating the constants and the disable branch in the bundled
JS of the 2.1.217 binary (minified identifiers above are from that build).
Environment
- Claude Code: 2.1.217 (CLI)
- OS: macOS (Darwin 25.5.0)
- Terminal: Ghostty + tmux (TERM=tmux-256color)
- gh: authenticated, standard
git@github.com:remote - Session shape when observed:
--worktree-style session (repo-root start,
EnterWorktree into .claude/worktrees/...)
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗