[BUG] OAuth token refresh race condition when running multiple Claude Code agents in parallel
Bug Description
When running multiple Claude Code processes in parallel (e.g. multiple claude --print --permission-mode bypassPermissions agents for different services in a monorepo), concurrent token refresh attempts cause a race condition that invalidates all active sessions, requiring manual re-login.
Environment
- Claude Code version: latest (2026-04)
- OS: Linux (Amazon Linux 2023, x86_64)
- Auth method: OAuth (subscription)
- Concurrent agents: 3–5 parallel
claude --printprocesses
Steps to Reproduce
- Set up a multi-service monorepo (e.g. 5 services: API, frontend, poster-builder, storefront, monitoring)
- Run 3–5 parallel Claude Code agents simultaneously:
``bash``
claude --print --permission-mode bypassPermissions 'Build service A' &
claude --print --permission-mode bypassPermissions 'Build service B' &
claude --print --permission-mode bypassPermissions 'Build service C' &
wait
- Let agents run for ~1–2 hours until token nears expiry
- Observe: multiple agents hit token expiry at the same time and all attempt to refresh concurrently
Expected Behavior
Only one process should perform the token refresh at a time. Other processes should:
- Wait for the refresh to complete, then use the new token
- OR detect that a refresh is already in progress (via lock file) and back off with retry
Actual Behavior
- Multiple processes simultaneously read the stale token from
~/.claude/.credentials.json - Each calls the refresh endpoint with the same
refreshToken - The first one to succeed invalidates the refresh token (OAuth refresh tokens are single-use)
- All other processes get
invalid_grantand wipecredentials.json - All agents die with "Not logged in · Please run /login"
- Entire parallel workflow is lost — hours of work gone
Impact
This is a critical blocker for AI-assisted multi-agent workflows — exactly the use case Claude Code is being marketed for (agentic development, parallel task execution). Users running Claude Code orchestration at scale hit this consistently.
Suggested Fix
Implement a file-based mutex for token refresh in ~/.claude/.refresh.lock:
// Pseudocode
async function refreshTokenWithLock(credentials) {
const lockFile = path.join(CLAUDE_DIR, '.refresh.lock');
// Try to acquire lock (atomic write)
try {
await fs.writeFile(lockFile, process.pid.toString(), { flag: 'wx' }); // exclusive
} catch (e) {
// Lock held by another process — wait and re-read credentials
await sleep(500);
return readCredentials(); // another process already refreshed
}
try {
const newToken = await doRefresh(credentials.refreshToken);
await writeCredentials(newToken);
return newToken;
} finally {
await fs.unlink(lockFile); // release lock
}
}
Alternatively, use a short TTL check: if credentials.json was modified in the last 5 seconds, skip refresh and re-read the file (another process just refreshed).
Related Issues
- #24317 — same root cause (race condition with 5+ concurrent sessions)
- #37402 —
--printmode doesn't persist refresh token - #33995 — WSL daily re-login (likely same underlying issue)
cc @anthropics/claude-code
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗