[BUG] Desktop app: OAuth refresh is reactive-only — every ~2h token rotation surfaces as "Request failed · retrying (1/10)" with exponential backoff across all active sessions
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
Environment: Claude desktop app 1.30096.5 (macOS, Darwin 25.5.0), bundled Claude Code CLI 2.1.229, Claude Max subscription (OAuth, no API key).
The desktop app's OAuth layer (oauth-v2) caches the access token together with its expiresAt, but it only refreshes the token reactively, after an in-flight API request has already failed with 401 — never proactively before expiry. Since access tokens rotate roughly every 2 hours, every long-running session predictably hits this wall:
- The access token expires mid-session (every ~2h).
- The next
/v1/messagesrequest fails with401 {"type":"error","error":{"type":"authentication_error","message":"OAuth access token has expired. Re-authenticate to continue."}}. - The 401 is fed into the generic retry machinery shared with 429/529/network errors, so the UI shows the alarming banner
Request failed · retrying (1/10)with exponential backoff — I have observed a 37-second wait on retry attempt 1 for what is a fully self-inflicted, instantly-fixable auth condition. - Because the credential is shared, all concurrently active sessions fail at the same instant (my transcripts show 2–4 sessions logging the identical 401 in the same second).
- The refresh then succeeds and the session recovers — so nothing is lost, but every ~2h every active session visibly "fails" and stalls for seconds to tens of seconds.
Evidence from ~/Library/Logs/Claude/main.log
The ~2h cadence of reactive refreshes (each line = a user-visible "Request failed · retrying" banner in at least one session):
2026-08-16 21:05:14 [info] [oauth] CLI requested token refresh after 401
2026-08-16 23:00:04 [info] [oauth] CLI requested token refresh after 401
2026-08-17 05:06:37 [info] [oauth] CLI requested token refresh after 401
2026-08-17 07:00:46 [info] [oauth] CLI requested token refresh after 401
2026-08-17 09:14:34 [info] [oauth] CLI requested token refresh after 401
2026-08-17 13:34:26 [info] [oauth] CLI requested token refresh after 401
2026-08-17 13:34:26 [info] [oauth-v2] refreshing token for orgId=<redacted>
2026-08-17 14:11:58 [info] [oauth] CLI requested token refresh after 401
Session transcript (~/.claude/projects/.../*.jsonl) — the same instant, two sessions:
2026-08-17T05:34:26.160Z api_error 401 OAuth access token has expired. Re-authenticate to continue. (session A)
2026-08-17T05:34:26.160Z api_error 401 OAuth access token has expired. Re-authenticate to continue. (session B)
Note the app-side cache clearly knows the expiry: on startup it logs [oauth] loaded token cache from disk with 4 entries including expiry metadata, yet no proactive refresh is ever scheduled.
Expected Behavior
Two independent fixes, either of which would remove the user-visible failure:
- Proactive refresh: the oauth-v2 layer knows
expiresAt; it should refresh the token N minutes before expiry (or on a timer) so in-flight requests never see a stale credential. This is the standard pattern for short-lived access tokens. - Fast-path 401 handling:
authentication_error401 should not share the exponential-backoff retry path with 429/529. The correct handling is: refresh token immediately → retry immediately, silently. A predictable credential rotation should never render asRequest failed · retrying (1/10)with a 37s backoff, and ideally should not be surfaced to the user at all when the refresh succeeds.
Related issues (searched, none covers this)
- #82217 — same "shared credential expires, all sessions fail simultaneously" observation, but scoped to CLI subagent propagation.
- #85618 — false-positive 401s caused by network interruptions (different trigger; that one does not self-heal).
- #34306 (closed) — startup-time refresh; this report is about mid-session rotation in the desktop app.
Steps to Reproduce
- Sign in to the Claude desktop app with a claude.ai subscription (OAuth).
- Keep one or more Claude Code sessions active for over ~2 hours.
- Observe: at the next token rotation, every active session shows
Request failed · retrying (1/10)with exponential backoff, and the transcript logsapi_error401OAuth access token has expired, followed by successful recovery.