Repository navigation
[Bug]: macOS: Claude usage stuck on "deferred-by-live-session" forever — the live-PTY refresh gate never opens when a Claude pane is always running #20251
Description
Operating system
macOS
Orca version
v1.4.200
Details
Short summary
On a machine that always has at least one Orca-launched claude pane alive (agent swarms — 8 live claude processes here, the oldest running 4 days 18 h), the Claude usage meter goes empty roughly 8 hours after every sign-in and never recovers on its own. The account is not lost: it stays Active, its oauth-account.json and .orca-managed-claude-auth marker are intact, and orca account list --json still lists it. Only usage/rate-limit data is dead. The only recovery is Settings → Accounts → Re-authenticate, repeated every ~8 h. It reads as "Orca loses my Claude account on every restart/update" because usage state is not persisted (#14833), so a relaunch is when you notice.
The live-PTY deferral introduced in #7483 and partially addressed by #9324/#9325 is an absorbing state for this class of user: the gate blocks the only self-refresh path, so the managed token can never become fresh again, so the gate condition can never become false.
There is a second, sharper half that I have not seen reported: when the live panes run with a different CLAUDE_CONFIG_DIR than the Orca main process, the documented escape from the deferral is unreachable by construction, not merely unlikely.
Measured state (orca account list --json, redacted)
"claude": { "provider":"claude", "session": null, "weekly": null,
"updatedAt": 1789189031294,
"error": "Claude usage refresh is waiting for the live Claude terminal to rotate its credentials.",
"status": "error",
"usageMetadata": { "attemptedSources":["oauth"], "failureKind":"deferred-by-live-session",
"credentialSource":"scoped-keychain", "authProvenance":"managed:<account-A>",
"deferredByLiveClaudeSession": true } }"codex" in the same call returns status: "ok" with real numbers, so the usage subsystem itself is healthy. Two managed Claude accounts, both authMethod: "subscription-oauth", managedAuthRuntime: "host"; the active one is a personal Max subscription. Nothing is signed out. Sampling updatedAt over a minute shows automatic fetches still running and returning the identical error each time.
Why it is self-sustaining (read from the shipped 1.4.200 bundle, out/main/index.js)
- In
doSyncForCurrentSelection:withlet l = hasLiveClaudePtys(); if (l && isTokenStale(c)) this.managedRefreshDeferredByLivePtyAccountId = i.id; if (!l) { const r = await this.refreshManagedAccountTokenIfNeeded(i, c); if (r) c = r; }
hasLiveClaudePtys(){return Bd.size>0}andisTokenStaletrue within 300 s of expiry. The two branches are mutually exclusive: Orca's owngrant_type=refresh_tokencall is reachable only when zero Claude panes are live. With a permanently non-empty set it is dead code — the token stays expired, soisTokenStale()stays true, so the deferral re-arms on every sync. - The classification is terminal with fallbacks disabled:
{failureKind, shouldAttemptCliFallback:false, shouldAttemptDelegatedRefresh:false, terminal:true}. The CLI fallback and delegated refresh that rescue other failure kinds are skipped. - The only automatic exit,
refreshAfterClaudeLivePtysDrained()(fix(rate-limits): refetch claude usage when the last live Claude PTY exits #9325), fires on the live-PTY count reaching 0. For a swarm user that edge never occurs. - The other documented exit — "the live Claude terminal rotates its credentials", then Orca reads them back — depends on the live CLI writing into Orca's own runtime surface.
getRuntimePaths()uses the main process'sCLAUDE_CONFIG_DIRor~/.claude, and read-back inspects only the scoped keychain item for that dir, the baseClaude Code-credentialsitem, and<configDir>/.credentials.json. - Retries do run (focus-gated ~15-min poll plus 30 s→15 min error backoff); they re-enter the same sync and return the same error.
fetchClaudeOnly({force:true})bypasses throttling, not the gate. I could find no timeout, deadline or TTL on the gate anywhere in the bundle, and no setting to disable it.
The config-dir half, measured
The scoped keychain service name is Claude Code-credentials-<sha256(configDir)[:8]>. Recomputed locally, the hashes match the items present on this machine exactly:
~/.claude 252ab343 <- Orca's surface (main process has no CLAUDE_CONFIG_DIR)
~/.claude-personal c0f3fb80 <- what the live panes actually rotate
~/.claude-work c311fa4b <- ditto, work profile
Keychain modification times, taken together:
Claude Code-credentials-c0f3fb80 2026-09-11 22:08Z <- live CLI rotating
Claude Code-credentials-c311fa4b 2026-09-11 22:41Z <- live CLI rotating
Claude Code-credentials-252ab343 2026-09-12 05:06Z <- Orca, rewriting its own
Claude Code-credentials 2026-09-12 05:06Z <- same second, base item
ps eww over the 8 live panes shows every one of them running with CLAUDE_CONFIG_DIR set to ~/.claude-personal or ~/.claude-work (a per-directory profile switcher in the user's shell rc — a common multi-profile setup), while the Orca main process has it unset. So the CLIs rotate every few hours into items Orca never reads, and the rotation Orca is waiting for is unobservable to it in principle. The same mismatch also silently disables the live status-line fallback, which drops the event with configDir mismatch.
Note the managed master item for the active account had not been written for ~24 h at diagnosis time, against an 8 h access-token TTL — the refresh token itself is still valid for weeks.
Steps to reproduce
- macOS, managed Claude account (subscription OAuth), active.
- Open a
claudepane in Orca and leave it running past 8 h (an agent session, a long task). - Wait for the managed access token to hit its 8 h expiry.
- Usage goes empty;
orca account list --jsonreportsfailureKind: "deferred-by-live-session". It stays that way indefinitely — through window focus, background polls, app restarts and version updates. - Settings → Accounts → Re-authenticate → numbers return for ~8 h → repeat.
Sharper variant that makes point 4 of the mechanism unavoidable: run the Claude panes with CLAUDE_CONFIG_DIR pointing somewhere other than the Orca main process's config dir. No rotation can then ever be read back, so step 4 is permanent by construction rather than by luck.
Expected behavior
Any one of these would break the loop:
- Put a deadline on the deferral: after N minutes of waiting with an already-expired token, perform Orca's own
refreshClaudeOauthCredentialsinstead of deferring further. That path is pure HTTP and needs no PTY — the same ask as [Bug]: Windows: Claude usage shows "OAuth access token has expired. Re-authenticate" — Orca never refreshes the managed token (refresh token still valid, no live PTY, no recovery path on win32) #18463 from the opposite direction. - Do not defer to a session whose credential surface Orca cannot read: if a live pane's
CLAUDE_CONFIG_DIRdiffers fromgetRuntimePaths().configDir, the read-back exit is unreachable, so the gate should not arm for it. - Re-check on each poll whether the awaited rotation actually landed, and stop waiting once the managed token is past expiry rather than merely near it — instead of keying recovery solely on the 1→0 PTY edge.
- Failing all of the above, say so in the UI: "waiting for the live Claude terminal" is indistinguishable from a hang when the wait can never end, especially with no last-known values kept ([Bug]: Claude usage should persist last-known 5h/weekly for every saved account #14833).
Actual behavior
The deferral re-arms on every sync, the terminal classification disables every fallback, the sole automatic exit requires closing every Claude terminal at once, and the user re-authenticates by hand every ~8 hours.
Related
- Usage panel stuck on "Waiting for Claude session" after the last live Claude terminal exits #9324 / fix(rate-limits): refetch claude usage when the last live Claude PTY exits #9325 — same mechanism; the merged fix covers only the last-PTY-exits edge
- Keep Claude live-PTY refresh gate closed across restarts; recover wiped runtime credentials (STA-1246) #7483 — introduced the persisted, pessimistically-seeded gate
- [Bug]: Windows: Claude usage shows "OAuth access token has expired. Re-authenticate" — Orca never refreshes the managed token (refresh token still valid, no live PTY, no recovery path on win32) #18463 — mirror case on Windows (no live PTY, still never refreshed)
- [Bug]: (Does not affect normal use) Claude Code usage status stuck on "Refreshing sign-in" and does not update #8974 / fix(status-bar): escalate stuck Claude "Refreshing sign-in" after timeout #10060 — stuck "Refreshing sign-in" (different
failureKinds) - [Bug]: Claude usage should persist last-known 5h/weekly for every saved account #14833 — no persisted last-known usage, which is why this renders as empty
- [Bug]: anthropic 401 #9582 / [Bug] frequent claude code login for multi account setup #13165 — multi-store token lineage and clobber hazards in the same subsystem
Activity
AmethystLiang commentedon Sep 15, 2026
Thanks for the detailed analysis and traces. The live-PTY gate in
live-pty-gate.tswas designed to avoid double-rotating single-use OAuth tokens while Claude CLI sessions are running, but becausenotifyDrainedOnTransitiononly fires when the live session count drops to 0, persistent or swarm sessions prevent the gate from ever reopening. Combined withCLAUDE_CONFIG_DIRmismatches preventing read-backs, usage enters an absorbingdeferred-by-live-sessionstate. We're looking into adding a deadline to the deferral and verifying config directory alignment before holding the refresh gate.mmarabel commentedon Sep 21, 2026
Another data point, from a setup with no agent swarm: same symptom, same recovery.
What I see: the Claude segment disappears from the status bar while the Codex meter sitting next to it keeps updating normally. The CLI itself is healthy, a
claudepane opens into a signed-in session with a working account, but the meter never comes back on its own. Re-authenticating in Settings → Accounts is the only thing that restores it, and it has to be repeated.The part I think is worth adding: launching
claudein a terminal does not clear it. That is the opposite of the sibling Codex case in #20118, where launchingcodexon the machine running the Orca UI brought that meter straight back. Under the mechanism described here the asymmetry is expected, since a live Claude pane is what arms the gate. But it makes the intuitive recovery ("just run the CLI and let it refresh its own token") actively counterproductive, and nothing in the UI hints at that.The code on current
main(7e2ebac318) still matches this report:src/main/claude-accounts/live-pty-gate.ts—notifyDrainedOnTransitionreturns early unlessliveClaudePtyIds.size === 0, so the only automatic exit is still the 1 → 0 edge.src/main/claude-accounts/runtime-auth/runtime-auth-sync.ts:245—if (liveClaudePtys && isOauthTokenExpiring(...))records the deferral, and the actual refresh sits in the mutually exclusiveif (!liveClaudePtys)branch.No deadline or TTL on the wait anywhere in between. #21935 arrives at the same two lines from the auth side and adds that every reader of
managedRefreshDeferredByLivePtyonly rewrites how the usage failure is reported, so the skipped refresh is recorded and then cleared by the next sync without ever being retried.Caveat on my own report: I have not yet captured
orca account list --jsoninside the failing window, so I can't confirmfailureKind: "deferred-by-live-session"versus themissing-credentialspath in #20118. I'll post the JSON next time it goes empty. The behavioural signature (live pane present, CLI signed in, re-authenticate as the only exit) points here rather than there.nwparker commentedon Oct 24, 2026
Thanks for the detailed report @jokius. The live-session gate that caused 'deferred-by-live-session' was removed in #26801, so a running Claude pane can no longer hold usage in that state. Could you retry on the latest build? If the Claude meter still disappears or needs re-authentication, please reply or reopen with your Orca version and what the status bar shows.
nwparker commentedon Oct 26, 2026
Thanks for the detailed report and analysis, @jokius. This is fixed on main by #26801 (8b2a5e4), which shipped in v1.4.225: the live-PTY refresh gate (
live-pty-gate.ts) was removed entirely, so a running Claude pane can no longer hold usage in thedeferred-by-live-sessionstate, regardless of how many panes stay alive or whichCLAUDE_CONFIG_DIRthey use. If you still see the Claude meter go empty or need re-authentication on the latest build, please comment here with your Orca version and what the status bar shows and we'll reopen.