Skip to content

[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

Closed

Description

@jokius

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)

  1. In doSyncForCurrentSelection:
    let l = hasLiveClaudePtys();
    if (l && isTokenStale(c)) this.managedRefreshDeferredByLivePtyAccountId = i.id;
    if (!l) { const r = await this.refreshManagedAccountTokenIfNeeded(i, c); if (r) c = r; }
    with hasLiveClaudePtys(){return Bd.size>0} and isTokenStale true within 300 s of expiry. The two branches are mutually exclusive: Orca's own grant_type=refresh_token call is reachable only when zero Claude panes are live. With a permanently non-empty set it is dead code — the token stays expired, so isTokenStale() stays true, so the deferral re-arms on every sync.
  2. 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.
  3. 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.
  4. 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's CLAUDE_CONFIG_DIR or ~/.claude, and read-back inspects only the scoped keychain item for that dir, the base Claude Code-credentials item, and <configDir>/.credentials.json.
  5. 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

  1. macOS, managed Claude account (subscription OAuth), active.
  2. Open a claude pane in Orca and leave it running past 8 h (an agent session, a long task).
  3. Wait for the managed access token to hit its 8 h expiry.
  4. Usage goes empty; orca account list --json reports failureKind: "deferred-by-live-session". It stays that way indefinitely — through window focus, background polls, app restarts and version updates.
  5. 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:

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

Activity

  1. AmethystLiang commented on Sep 15, 2026

    @AmethystLiang
    Contributor

    Thanks for the detailed analysis and traces. The live-PTY gate in live-pty-gate.ts was designed to avoid double-rotating single-use OAuth tokens while Claude CLI sessions are running, but because notifyDrainedOnTransition only fires when the live session count drops to 0, persistent or swarm sessions prevent the gate from ever reopening. Combined with CLAUDE_CONFIG_DIR mismatches preventing read-backs, usage enters an absorbing deferred-by-live-session state. We're looking into adding a deadline to the deferral and verifying config directory alignment before holding the refresh gate.

  2. added theissue type on Sep 19, 2026
  3. mmarabel commented on Sep 21, 2026

    @mmarabel
    Contributor

    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 claude pane 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 claude in a terminal does not clear it. That is the opposite of the sibling Codex case in #20118, where launching codex on 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 — notifyDrainedOnTransition returns early unless liveClaudePtyIds.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 exclusive if (!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 managedRefreshDeferredByLivePty only 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 --json inside the failing window, so I can't confirm failureKind: "deferred-by-live-session" versus the missing-credentials path 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.

  4. nwparker commented on Oct 24, 2026

    @nwparker
    Contributor

    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.

  5. nwparker commented on Oct 26, 2026

    @nwparker
    Contributor

    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 the deferred-by-live-session state, regardless of how many panes stay alive or which CLAUDE_CONFIG_DIR they 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingos:macosIssues affecting macOS

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions