CI monitoring widget shows "CI checks unavailable, check gh installed" despite gh being installed/authenticated and PR checks passing

Status Open
Maintainer reply None cached
Activity 4 comments · opened Jul 16, 2026

Description

The CI monitoring feature (the popover shown when clicking the CI status chip on a PR reference in the session) reports:

CI checks unavailable Check that gh is installed and authenticated.

This is incorrect — gh is installed, authenticated, and working, and the PR's checks are all passing on GitHub itself.

Environment

  • macOS (Apple Silicon)
  • gh version 2.96.0, installed via Homebrew at /opt/homebrew/bin/gh
  • gh auth status succeeds (logged in via keyring)
  • Confirmed via gh pr checks <PR> from a terminal that all checks pass (GitHub Actions, Bitrise, CodeQL, CodeRabbit, DevBot Linter)
  • Confirmed via the GitHub web UI (.../pull/<PR>/checks) that all checks are green

Troubleshooting already done

  • Restarted the Claude Code app — no change
  • Verified gh resolves under a bare launchd-style PATH (/usr/local/bin:/bin:/usr/bin) by symlinking gh into /usr/local/bin — no change
  • Verified gh auth status succeeds when HOME is set (ruling out a stripped-environment auth issue) — no change

None of these environment-level fixes resolved the widget, suggesting the "check gh is installed" message is either a generic/misleading fallback, or the underlying check isn't actually shelling out to the user's local gh the way it's described.

Expected behavior

The CI monitoring widget should reflect the actual (passing) CI status shown on GitHub, or at minimum surface a more specific error than a generic "check gh is installed" message when gh is demonstrably installed and working.

View original on GitHub ↗

4 Comments

yitong-song · 1 month ago

+1

zachintosh · 1 month ago

+1

imartinez-traackr · 1 month ago

Deep-dive from another affected machine: I instrumented the app from outside and verified every layer individually healthy — including capturing the app's own gh spawn succeeding — yet the widget still reports unavailable. Evidence points at the renderer layer, plus a plausible trigger matching #59182 (session cwd is not a git repo).

Environment: Claude desktop 1.24012.9 (bundled CLI 2.1.219), macOS 26.5 (Intel), gh 2.92.0 at /usr/local/bin/gh, keyring auth. PRs on a private org repo; gh pr checks from a terminal shows all checks green.

What I verified:

  1. The app finds and runs gh successfully. I placed a transparent logging shim as the first gh on PATH. On app launch, the main process spawned gh auth token --hostname github.com (env AI_AGENT=claude-desktop_1-24012-9_harness, cwd=$HOME) — exit 0, 41-byte token on stdout, empty stderr. So PATH resolution, spawning, and keychain access all work. (Current builds also carry /usr/local/bin and /opt/homebrew/bin as hardcoded fallback search paths, so the stripped-launchd-PATH theory from the OP can't be the cause on 1.24012.x.)
  2. Electron's network stack reaches GitHub. Observed an ESTABLISHED TCP connection from the app's network process to the github.com load balancer, and api.github.com returns a proper 401 JSON when loaded from the in-app browser pane. The REST timeout in the bundle is 15s; observed latency ~0.5s.
  3. Folder trust passes. LocalSessions.checkGhAvailable: cwd=... is logged once per app launch (at session warm) with a cwd whose hasTrustDialogAccepted is true in ~/.claude.json. Side note for anyone else debugging this message: if the PR's repo root is registered with hasTrustDialogAccepted: false in ~/.claude.json (background/non-interactive agents can register folders that way), the widget shows this same message without ever checking gh — fixing that flag was necessary but not sufficient on my machine.
  4. The widget never re-checks. The availability check fires exactly once per app launch: opening the popover, switching sessions, or waiting does not produce further checkGhAvailable log lines, no further gh spawns through the shim, and LocalSessions.getPrChecks is never invoked at all. Whatever the renderer's availability query computes at launch is cached for the app's lifetime, and the two automation checkboxes stay disabled.

Possibly the trigger (matches #59182): my session cwd is a parent folder that contains multiple repos but is itself not a git repository; the bound PRs belong to a repo one level below. If the availability probe or the PR lookup runs any git operation against the session cwd, it would fail there and may be mapped to this generic gh message.

Timing datapoint: on this same machine the GitHub REST/auth layer demonstrably worked on Jun 9 (GhRestClient GraphQL responses in main.log); the widget is consistently broken on the July builds.

Ruled out: Full Disk Access / macOS TCC, system proxy (none configured), /etc/hosts entries, gh hosts.yml (github.com is default-known), API rate limiting (4991/5000 remaining), and stale renderer persistence (no persisted query cache on disk — the wrong verdict is recomputed fresh on every launch).

Happy to run further diagnostics if useful.

wshallwshall · 1 month ago

Windows datapoint with a confirmed fix — likely a different trigger than
@imartinez-traackr's, which I think supports the theory that this one string
has several distinct causes behind it.

Environment: Claude desktop 1.24012.9 (bundled CLI 2.1.219), Windows 11 Pro
26200, installed as an MSIX/Store package
(C:\Program Files\WindowsApps\Claude_1.24012.9.0_x64__pzs8sxrjxfjjc).
gh 2.93.0 at C:\Program Files\GitHub CLI\gh.exe, on the Machine PATH.

Fix: gh auth login --hostname github.com --insecure-storage. The widget
populated immediately. Notably gh reported You were already logged in to this
account
— the identity never changed, only the token's storage backend
(keyring → plaintext hosts.yml).

Why I think it's the credential store, not the token:

  • Before the change, hosts.yml was 92 bytes: github.com: / users: and no

token at all. The token was keyring-only.

  • That keyring token was valid — gh auth status and gh pr checks <PR>

(20+ checks returned) both succeeded from a normal shell while the widget
was reporting unavailable.

  • After --insecure-storage, hosts.yml is 216 bytes with oauth_token, and

gh auth status reports the source as the file rather than (keyring).

  • The failure signature is distinctive: a gh process that can't reach the

credential store still finds the account and prints
X Failed to log in to github.com account <user> (keyring) — which is
neither "not installed" nor "not authenticated".

On the message itself. In resources/ion-dist/assets/v1/c360a9e1c-*.js
the popover renders five states — gh_not_installed, gh_not_authenticated,
rate_limited, network, and unknown (which renders e.detail) — with
"Check that gh is installed and authenticated." as the ?? fallback when the
reason is absent or unknown carries no detail. So the app was never claiming
gh was missing; it was saying it didn't know. gh's own stderr names the cause
exactly and appears to be discarded.

Two suggestions, both cheap:

  1. Pass gh's stderr through into unknown's detail — the renderer already

displays it, it's just arriving empty. That alone would have made this
self-diagnosing.

  1. Given #59182, #69101, #79599, #67087 and this all surface the same string,

the fallback's advice is actively misleading often enough that it may be
worth making it non-committal ("couldn't determine CI status") rather than
naming a cause it hasn't verified.

Corroborates @imartinez-traackr's point 4: the check is cached per app launch,
so a restart was required after re-authenticating before the widget updated.