Bug: Frequent re-authentication required with multiple concurrent Claude Code sessions
Description
Claude Code requires re-authentication (browser OAuth flow) multiple times per day, even though a valid refresh token exists in ~/.claude/.credentials.json. This appears to be caused by a race condition when multiple concurrent Claude Code sessions attempt to refresh the same OAuth token.
Environment
- Claude Code version: 2.1.37
- OS: macOS 15.7.1 (Apple Silicon, arm64)
- Subscription: Team (Max 5x tier)
- Concurrent sessions: 7-12 active Claude Code processes at any given time
Steps to Reproduce
- Open 5+ terminal tabs, each running
claude in different project directories
- Work normally across sessions throughout the day
- After some time (typically a few hours), one or more sessions will prompt:
/login to re-authenticate
- Re-authenticating in one session may cause other sessions to lose their auth shortly after
Expected Behavior
The refresh token should silently renew the access token without user interaction. Multiple concurrent sessions sharing the same credentials file should coordinate token refresh (e.g., file-based locking, or re-reading the credentials file before attempting refresh).
Actual Behavior
Sessions frequently lose authentication and require a full browser-based OAuth re-login. This happens multiple times per day with several concurrent sessions open.
Root Cause Analysis
The credentials file (~/.claude/.credentials.json) contains:
accessToken — short-lived (~15 hour expiry)
refreshToken — used to obtain new access tokens
OAuth refresh tokens are typically single-use: when one process uses the refresh token to get a new access/refresh token pair, the old refresh token is invalidated server-side. With N concurrent Claude Code processes:
- Process A's access token expires
- Process A uses the refresh token → gets new access token + new refresh token
- Process A writes new credentials to
~/.claude/.credentials.json
- Process B's access token also expires (or was already expired)
- Process B tries to use the old refresh token (which it read into memory at startup)
- Server rejects the old refresh token (already consumed by Process A)
- Process B prompts user to re-authenticate via browser
This race becomes increasingly likely with more concurrent sessions, as the window for token expiry overlap grows.
Impact
- Disrupts workflow — interrupts active coding sessions to complete browser OAuth flow
- Blocks headless/SSH use cases entirely — Claude Code cannot be used over SSH when re-auth is needed, because the OAuth flow opens a browser on the host machine. This makes remote access (e.g., from a mobile SSH client) unreliable.
- Scales with usage — power users who keep multiple sessions open (different projects, different terminal tabs) are most affected
Suggested Fixes
-
Re-read credentials from disk before refresh: Before attempting a token refresh, check if another process has already written a fresh token to the credentials file. If the file's expiresAt is in the future, use the new token instead of refreshing.
-
File-based locking on refresh: Use a lockfile (~/.claude/.credentials.lock) to prevent concurrent refresh attempts. Only one process refreshes at a time; others wait and then read the refreshed token.
-
Longer-lived refresh tokens: If the refresh token had a longer lifetime or was multi-use (not invalidated on first use), concurrent sessions could independently refresh without invalidating each other.
-
In-memory token sharing: Use a local IPC mechanism (Unix socket, shared memory) so all Claude Code processes share a single token manager that handles refresh.
Workaround
Currently, the only workaround is to close stale Claude Code sessions to reduce the number of concurrent processes competing for token refresh. This is not ideal for users who work across multiple projects simultaneously.
An ANTHROPIC_API_KEY environment variable bypasses OAuth entirely but uses API billing instead of the subscription — not a real fix for subscription users.
Additional Context
This bug is particularly impactful for users building automation around Claude Code (e.g., LaunchAgent-triggered sessions, SSH-based remote access from mobile devices) where browser-based re-authentication is impossible.
Bug: Frequent re-authentication required with multiple concurrent Claude Code sessions
Description
Claude Code requires re-authentication (browser OAuth flow) multiple times per day, even though a valid refresh token exists in
~/.claude/.credentials.json. This appears to be caused by a race condition when multiple concurrent Claude Code sessions attempt to refresh the same OAuth token.Environment
Steps to Reproduce
claudein different project directories/loginto re-authenticateExpected Behavior
The refresh token should silently renew the access token without user interaction. Multiple concurrent sessions sharing the same credentials file should coordinate token refresh (e.g., file-based locking, or re-reading the credentials file before attempting refresh).
Actual Behavior
Sessions frequently lose authentication and require a full browser-based OAuth re-login. This happens multiple times per day with several concurrent sessions open.
Root Cause Analysis
The credentials file (
~/.claude/.credentials.json) contains:accessToken— short-lived (~15 hour expiry)refreshToken— used to obtain new access tokensOAuth refresh tokens are typically single-use: when one process uses the refresh token to get a new access/refresh token pair, the old refresh token is invalidated server-side. With N concurrent Claude Code processes:
~/.claude/.credentials.jsonThis race becomes increasingly likely with more concurrent sessions, as the window for token expiry overlap grows.
Impact
Suggested Fixes
Re-read credentials from disk before refresh: Before attempting a token refresh, check if another process has already written a fresh token to the credentials file. If the file's
expiresAtis in the future, use the new token instead of refreshing.File-based locking on refresh: Use a lockfile (
~/.claude/.credentials.lock) to prevent concurrent refresh attempts. Only one process refreshes at a time; others wait and then read the refreshed token.Longer-lived refresh tokens: If the refresh token had a longer lifetime or was multi-use (not invalidated on first use), concurrent sessions could independently refresh without invalidating each other.
In-memory token sharing: Use a local IPC mechanism (Unix socket, shared memory) so all Claude Code processes share a single token manager that handles refresh.
Workaround
Currently, the only workaround is to close stale Claude Code sessions to reduce the number of concurrent processes competing for token refresh. This is not ideal for users who work across multiple projects simultaneously.
An
ANTHROPIC_API_KEYenvironment variable bypasses OAuth entirely but uses API billing instead of the subscription — not a real fix for subscription users.Additional Context
This bug is particularly impactful for users building automation around Claude Code (e.g., LaunchAgent-triggered sessions, SSH-based remote access from mobile devices) where browser-based re-authentication is impossible.