[BUG] OAuth token refresh race condition when running multiple Claude Code agents in parallel

Resolved 💬 3 comments Opened Apr 4, 2026 by wiliyam Closed May 8, 2026

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 --print processes

Steps to Reproduce

  1. Set up a multi-service monorepo (e.g. 5 services: API, frontend, poster-builder, storefront, monitoring)
  2. 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
``

  1. Let agents run for ~1–2 hours until token nears expiry
  2. 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_grant and wipe credentials.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 — --print mode doesn't persist refresh token
  • #33995 — WSL daily re-login (likely same underlying issue)

cc @anthropics/claude-code

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗