Multi-session power user loses all sessions to refresh rotation races
Summary
Running many concurrent long-lived Claude Code sessions (deliberate multi-session
workflow: parallel worker sessions, remote control, background agents) eventually
kills authentication for ALL sessions at once, and interactive re-login does not
stick while the other sessions are still alive. The cause appears to be OAuth
refresh-token rotation raced by multiple processes sharing~/.claude/.credentials.json with no cross-process coordination.
Environment
- Claude Code 2.1.251, Windows 11 Pro for Workstations (10.0.26200)
- Subscription OAuth login (claude.ai), not API key
- Typical steady state: 5-18 live
claudeprocesses spanning several days
(parallel sessions, remote control, orphaned process trees from closed
terminals)
Observed evidence (captured while broken)
~/.claude/.credentials.jsonafter the failure:claudeAiOauth.expiresAt
and every mcpOAuth.*.expiresAt (11 plugin connectors) all read exactly
epoch zero (1970-01-01T00:00:00Z), while
claudeAiOauth.refreshTokenExpiresAt still held a valid future date
(~2 weeks out).
- File mtime was 3 seconds after a fresh
claudeprocess launch - a freshly
started session wrote the zeroed state.
- 18
claudeprocesses were alive, including a cluster of ~12 from one
session tree started 3 days earlier.
- One long-lived session (this one) kept working throughout on its in-memory
token while the file said everything was expired.
- Every OAuth-based MCP connector was simultaneously in the
"requires authentication" state at session start.
- File permissions were fine (user full control, not read-only), so
persistence itself was not the blocker.
What seems to happen
- Each process caches tokens in memory and independently refreshes when its
own expiry timer fires.
- Refresh tokens rotate on use, so the first process to refresh invalidates
the refresh token every other process holds in memory.
- A later process attempts refresh with the now-rotated token, fails, and its
session disconnects ("randomly" from the user's perspective - it is
whichever process's timer fires second).
- A failing/stale process appears able to write its state back over the shared
file, clobbering the winner's fresh token - after which even a brand-new
interactive /login gets clobbered again while stale processes live.
Recovery required hunting down and killing every stale claude process
before re-login would stick.
Expected
Concurrent sessions on one machine should share one token lifecycle: either a
single-owner refresh (broker/lock so exactly one process refreshes and everyone
re-reads), or at minimum read-modify-write with file locking plus re-read-
before-refresh so a rotated token is picked up from disk instead of retried
from memory. A stale process should never be able to overwrite newer
credentials.
Impact
- All sessions lose auth at once, including active remote-control sessions.
- Re-login appears broken (fails to stick) until the user manually kills every
stale process - very hard to self-diagnose.
- All plugin MCP OAuth connectors go down with it and each needs manual re-auth.
Repro sketch
- Log in via OAuth; start several long-lived
claudesessions and leave a
detached/orphaned process tree running (close a terminal without killing
children).
- Wait for access-token expiry cycles to pass across multiple days.
- Observe cascading session auth failures, then attempt
/loginwhile stale
processes are still alive - the new token gets invalidated/overwritten.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗