Skip to content

[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

Description

@Minidoracat

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

managed account A managed account B
.credentials.json mtime (%APPDATA%\orca\claude-accounts\<id>\auth\) 09:29:18 09:29:04
claudeAiOauth.expiresAt 17:29:16 17:29:02
claudeAiOauth.refreshTokenExpiresAt ~4 weeks out ~4 weeks out
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.jsonclaudeLivePtySessionIds: []; 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)

  1. 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.
  2. Background polling is focus-gated: service-polling.ts:60-69 shouldBackgroundPoll() requires isVisible() && !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.)
  3. 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.
  4. 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

  1. Windows, add a managed Claude account (subscription OAuth) and make it active.
  2. 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.
  3. Come back: the Claude usage chip shows OAuth access token has expired. Re-authenticate to continue.
  4. 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".

Related

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

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions