[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

Status Open
Reported on v2.1.217
Maintainer reply None cached
Activity 1 comment · opened Jul 22, 2026

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,
  • gh is installed, authenticated, and gh pr view succeeds 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 and
workspace.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:

  1. 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.

  1. The limit is below the subprocess timeout. The gh invocation itself is spawned

with a 5000 ms timeout, so any fetch in the 4–5 s window "succeeds" yet still
permanently kills the feature.

  1. 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. via
EnterWorktree) — 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):

  1. Re-enable the poller after a backoff instead of disabling it forever (treat the >4s

case like the existing miss/backoff handling).

  1. At minimum, clear the disabled flag on the documented explicit triggers (a gh pr or

git push command running in the session), so the state is user-recoverable.

  1. 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:

  1. Put a gh shim on PATH that delays one pr view call by ~4.5 s (under the 5 s spawn

timeout), then behaves normally.

  1. Start claude on a branch with an open PR. The first poll trips the 4 s check.
  2. Observe: the badge never appears for the rest of the session; running gh pr view in

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/...)

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗