You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[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
On Windows, Orca's status-bar Claude usage meter reports OAuth access token has expired. Re-authenticate to continue. even though the managed account's refresh token is still valid for weeks. Orca simply never refreshed the access token: both managed accounts were last refreshed at 09:29, the access tokens expired at 17:29 (8h TTL), and at 19:20 Orca was still running, still holding the expired tokens, with no live Claude PTY. Meanwhile a separate OAuth session for the same Anthropic account, kept by another tool with its own token store, was refreshed on schedule and returned 200 from the same usage endpoint the whole time — so the account itself is healthy.
This is distinct from #9582 / #13165 (macOS, multi-store clobber, token family revoked). No clobber happened here: ~/.claude/.credentials.json and the managed .credentials.json were byte-identical and both untouched since 09:29. The token is not broken, it is just never refreshed, and on Windows there is no recovery path after the resulting 401.
What happened (evidence, all timestamps local, today)
GET https://api.anthropic.com/api/oauth/usage with that access token at 19:21
401 {"type":"authentication_error","message":"OAuth access token has expired. Re-authenticate to continue."}
same 401
same account, independent OAuth session held by another local tool (own refresh token, refreshed at 19:17–19:22)
200, 5h/7d utilization returned
200
Other state at the time:
Orca.exe running (6 processes), main window in use (this was captured from a terminal inside Orca).
orca-data.json → claudeLivePtySessionIds: []; no claude / claude.exe process on the machine (only non-Claude agents in panes). So hasLiveClaudePtys() was false and the "wait for the live Claude terminal to rotate" deferral does not apply.
settings.activeClaudeManagedAccountId = account B; ~/.claude/.credentials.json is byte-identical to account B's managed file (Orca's materialized copy), mtime 09:29:25 — nothing else wrote it all day.
daemon.log and main.trace.ndjson* contain zero auth/refresh/usage entries, so from the filesystem it is impossible to tell whether the proactive refresh was never attempted or attempted and failed.
Why this can happen (reading origin/main today)
The only proactive refresh is refreshManagedAccountTokenIfNeeded inside doSyncForCurrentSelection (src/main/claude-accounts/runtime-auth/runtime-auth-sync.ts:244-257). Sync runs on startup, account switch, prepareForClaudeLaunch, and prepareForRateLimitFetch. If none of those fire for 8h, the token expires.
refreshClaudeOauthCredentials (src/main/claude-accounts/oauth-refresh.ts:141-170) is best-effort: any non-2xx or network error is console.warn + return null, the caller keeps the expired blob, and nothing records the failure in usageMetadata. On Windows console.warn from the main process goes nowhere the user can see.
Windows has no recovery path after the 401.service-fetch-targets.ts:133-146 returns false for shouldAllowClaudePtyFallback and shouldAllowClaudeUsagePanelSupplement on win32. In claude-active-usage-fetch.ts:98-118 both repairClaudeCredentialsThenRetryOAuth and fetchClaudeUsageViaCli are gated on allowCliFallback, so on Windows a stale-token classification goes straight to the error result. On macOS/Linux the PTY fallback would let the CLI rotate the token; on Windows the meter just says "Re-authenticate" while the refresh token is perfectly usable.
Net effect on Windows: Orca is the sole owner of this token lineage (it materializes it into ~/.claude itself), yet it only refreshes opportunistically, fails silently, and has no fallback. The "re-authenticate" prompt is misleading — a plain grant_type=refresh_token call would have fixed it.
How to reproduce
Windows, add a managed Claude account (subscription OAuth) and make it active.
Do not open a claude pane in Orca. Keep Orca running but work in other applications (or otherwise avoid the focus-gated poll) for > 8 hours.
Come back: the Claude usage chip shows OAuth access token has expired. Re-authenticate to continue.
Check %APPDATA%\orca\claude-accounts\<id>\auth\.credentials.json: expiresAt is in the past, refreshTokenExpiresAt is weeks away.
Expected
With a valid refresh token and no live Claude PTY, Orca should refresh the access token before/at expiry regardless of window focus, or at least on the first usage fetch that returns 401 — the pure-HTTP refreshClaudeOauthCredentials already exists and needs no PTY, so the Windows allowCliFallback gate should not block it.
A failed refresh should surface its HTTP status (or "network") in usageMetadata / the chip instead of collapsing into "Re-authenticate".
Happy to open a PR that adds a PTY-free delegated refresh path (call refreshClaudeOauthCredentials on the managed credentials, persist via writeClaudeManagedCredentialsJson, retry the OAuth usage request) and records refresh failures in usageMetadata, if maintainers are open to it given the pending auth PRs (#9754, #6864).
Operating system
Windows
Orca version
1.4.195 (Windows 11 Pro 24H2, x64)
Details
Short summary
On Windows, Orca's status-bar Claude usage meter reports
OAuth access token has expired. Re-authenticate to continue.even though the managed account's refresh token is still valid for weeks. Orca simply never refreshed the access token: both managed accounts were last refreshed at 09:29, the access tokens expired at 17:29 (8h TTL), and at 19:20 Orca was still running, still holding the expired tokens, with no live Claude PTY. Meanwhile a separate OAuth session for the same Anthropic account, kept by another tool with its own token store, was refreshed on schedule and returned200from the same usage endpoint the whole time — so the account itself is healthy.This is distinct from #9582 / #13165 (macOS, multi-store clobber, token family revoked). No clobber happened here:
~/.claude/.credentials.jsonand the managed.credentials.jsonwere byte-identical and both untouched since 09:29. The token is not broken, it is just never refreshed, and on Windows there is no recovery path after the resulting 401.What happened (evidence, all timestamps local, today)
.credentials.jsonmtime (%APPDATA%\orca\claude-accounts\<id>\auth\)claudeAiOauth.expiresAtclaudeAiOauth.refreshTokenExpiresAtGET https://api.anthropic.com/api/oauth/usagewith that access token at 19:21401 {"type":"authentication_error","message":"OAuth access token has expired. Re-authenticate to continue."}200, 5h/7d utilization returned200Other state at the time:
orca-data.json→claudeLivePtySessionIds: []; noclaude/claude.exeprocess on the machine (only non-Claude agents in panes). SohasLiveClaudePtys()was false and the "wait for the live Claude terminal to rotate" deferral does not apply.settings.activeClaudeManagedAccountId= account B;~/.claude/.credentials.jsonis byte-identical to account B's managed file (Orca's materialized copy), mtime 09:29:25 — nothing else wrote it all day.daemon.logandmain.trace.ndjson*contain zero auth/refresh/usage entries, so from the filesystem it is impossible to tell whether the proactive refresh was never attempted or attempted and failed.Why this can happen (reading
origin/maintoday)refreshManagedAccountTokenIfNeededinsidedoSyncForCurrentSelection(src/main/claude-accounts/runtime-auth/runtime-auth-sync.ts:244-257). Sync runs on startup, account switch,prepareForClaudeLaunch, andprepareForRateLimitFetch. If none of those fire for 8h, the token expires.service-polling.ts:60-69shouldBackgroundPoll()requiresisVisible() && !isMinimized() && isFocused(), 15-minute interval. Working in another window all afternoon means no fetch, no sync, no refresh. ([Bug]: Usage status bar stops auto-refreshing after an update relaunch (focus-gated poll), separate from the #5355 auth path #5627 was closed, but this gate is still there.)refreshClaudeOauthCredentials(src/main/claude-accounts/oauth-refresh.ts:141-170) is best-effort: any non-2xx or network error isconsole.warn+return null, the caller keeps the expired blob, and nothing records the failure inusageMetadata. On Windowsconsole.warnfrom the main process goes nowhere the user can see.service-fetch-targets.ts:133-146returnsfalseforshouldAllowClaudePtyFallbackandshouldAllowClaudeUsagePanelSupplementonwin32. Inclaude-active-usage-fetch.ts:98-118bothrepairClaudeCredentialsThenRetryOAuthandfetchClaudeUsageViaCliare gated onallowCliFallback, so on Windows astale-tokenclassification goes straight to the error result. On macOS/Linux the PTY fallback would let the CLI rotate the token; on Windows the meter just says "Re-authenticate" while the refresh token is perfectly usable.Net effect on Windows: Orca is the sole owner of this token lineage (it materializes it into
~/.claudeitself), yet it only refreshes opportunistically, fails silently, and has no fallback. The "re-authenticate" prompt is misleading — a plaingrant_type=refresh_tokencall would have fixed it.How to reproduce
claudepane in Orca. Keep Orca running but work in other applications (or otherwise avoid the focus-gated poll) for > 8 hours.OAuth access token has expired. Re-authenticate to continue.%APPDATA%\orca\claude-accounts\<id>\auth\.credentials.json:expiresAtis in the past,refreshTokenExpiresAtis weeks away.Expected
refreshClaudeOauthCredentialsalready exists and needs no PTY, so the WindowsallowCliFallbackgate should not block it.usageMetadata/ the chip instead of collapsing into "Re-authenticate".Related
claude setup-tokenfor managed Claude accounts #12002 —setup-tokensupport would sidestep refresh entirelyHappy to open a PR that adds a PTY-free delegated refresh path (call
refreshClaudeOauthCredentialson the managed credentials, persist viawriteClaudeManagedCredentialsJson, retry the OAuth usage request) and records refresh failures inusageMetadata, if maintainers are open to it given the pending auth PRs (#9754, #6864).