[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

Status Open
Reported on v2.1.229
Maintainer reply None cached
Activity 0 comments · opened Aug 17, 2026

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:

  1. The access token expires mid-session (every ~2h).
  2. The next /v1/messages request fails with 401 {"type":"error","error":{"type":"authentication_error","message":"OAuth access token has expired. Re-authenticate to continue."}}.
  3. 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.
  4. 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).
  5. 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:

  1. 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.
  2. Fast-path 401 handling: authentication_error 401 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 as Request 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

  1. Sign in to the Claude desktop app with a claude.ai subscription (OAuth).
  2. Keep one or more Claude Code sessions active for over ~2 hours.
  3. Observe: at the next token rotation, every active session shows Request failed · retrying (1/10) with exponential backoff, and the transcript logs api_error 401 OAuth access token has expired, followed by successful recovery.

View original on GitHub ↗