Windows: ~/.claude.json corruption from concurrent sessions (race condition in config writes)

Resolved 💬 4 comments Opened Feb 26, 2026 by viftode4 Closed Feb 26, 2026

Preflight Checklist

  • [x] I have searched the existing issues and this has not been reported before
  • [x] This is a single bug report, not multiple issues combined
  • [x] I am using the latest version of Claude Code

What's Wrong?

On Windows 11, running multiple concurrent Claude Code CLI sessions causes repeated corruption of ~/.claude.json. The file gets truncated mid-write, producing invalid JSON (Unexpected EOF). This causes:

  • JSON Parse error: Unexpected EOF on every session start
  • Loss of oauthAccount data, forcing re-login every session
  • Other active sessions breaking with display errors
  • Hundreds of .corrupted backup files accumulating in ~/.claude/backups/ (358 files over ~1 week)

What Should Happen?

Multiple concurrent Claude Code sessions should be able to safely read/write ~/.claude.json without corrupting each other's writes. Auth data (oauthAccount) should persist across sessions. The config file should never end up as truncated/invalid JSON.

Steps to Reproduce

  1. Open 3-4 terminal windows on Windows 11
  2. Run claude --dangerously-skip-permissions in each
  3. Use all sessions concurrently (the config gets written frequently due to toolUsage, clientDataCache, cachedGrowthBookFeatures updates)
  4. After a few minutes, observe:
  • JSON Parse error: Unexpected EOF errors appearing
  • Sessions losing auth and asking to re-login
  • .corrupted files accumulating in ~/.claude/backups/

Is this a regression?

Yes, this worked in a previous version

Claude Code Version

2.1.59

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Windows Terminal

Error Messages/Logs

Claude configuration file at C:\Users\levla\.claude.json is corrupted: JSON Parse error: Unexpected EOF
The corrupted file has been backed up to: C:\Users\levla\.claude\backups\.claude.json.corrupted.1772132075540
A backup file exists at: C:\Users\levla\.claude\backups\.claude.json.backup.1772132048488
You can manually restore it by running: cp "C:\Users\levla\.claude\backups\.claude.json.backup.1772132048488" "C:\Users\levla\.claude.json"

Claude Model

Not sure/Multiple models

Last Working Version

Not sure exactly, but this started occurring in the last few weeks. Possibly around 2.1.x.

Additional Information

Root Cause Analysis

Decompiled the bundled JS from the binary and traced the issue to the config write path:

1. Atomic write fallback (writeFileAtomic)
The atomic write correctly uses temp file + renameSync. On Windows, renameSync fails with EPERM when another process has the target file open. The code falls back to a direct non-atomic writeFileSync:

// Pseudocode from decompiled source
function writeFileAtomic(path, data) {
  let tempFile = `${path}.tmp.${process.pid}.${Date.now()}`;
  try {
    fs.writeFileSync(tempFile, data, { flush: true });
    fs.renameSync(tempFile, path);  // FAILS on Windows with EPERM
  } catch {
    // Fallback: non-atomic direct write — THIS IS THE BUG
    fs.writeFileSync(path, data, { flush: true });
  }
}

2. Lock contention (saveConfigWithLock)
Uses proper-lockfile with lockSync(). When lock acquisition fails, saveGlobalConfig catches the error and falls back to a completely unlocked, non-atomic write:

function saveGlobalConfig(callback) {
  try {
    saveWithLock(configPath, defaults, callback);
  } catch {
    // FALLBACK: unlocked write when lock fails
    saveWithoutLock(configPath, mergedConfig, defaults);
  }
}

3. The cascade

  1. Session A reads config → Session B reads config
  2. Session A acquires lock, writes (rename fails on Windows) → falls back to direct write
  3. Session B acquires lock (or fails), starts writing simultaneously
  4. One write truncates the other → corrupted JSON
  5. Session C starts, reads corrupted file → Unexpected EOF
  6. Corruption handler backs up corrupted file, writes minimal config (losing oauthAccount)
  7. All sessions now need re-login → more writes → more corruption
Evidence
  • 358 corrupted backup files in ~/.claude/backups/ over ~1 week
  • Corrupted files are truncated JSON (50-90KB files reduced to 50-157 byte fragments)
  • Multiple corrupted files with timestamps within the same second (concurrent writes)
  • oauthAccount consistently lost, while transient fields like toolUsage survive (written most frequently)
Suggested Fixes
  1. Don't fall back to non-atomic writes on Windows. Retry the rename with backoff, or use MoveFileEx with MOVEFILE_REPLACE_EXISTING.
  2. Use Windows-native file locking (LockFileEx / fs.open with exclusive flags) instead of proper-lockfile's directory-based locking which is unreliable on NTFS under high contention.
  3. Abort stale writes: tengu_config_stale_write telemetry already detects when the file changed since read — make it abort the write instead of proceeding.
  4. Separate volatile and stable config: Move frequently-written fields (toolUsage, clientDataCache, cachedGrowthBookFeatures) to a separate file to reduce write contention on the auth config.
Workaround

Created a PowerShell file watcher that monitors ~/.claude.json, detects corruption via JSON parse validation, and auto-restores from an in-memory snapshot within ~200ms. Also merges back oauthAccount when it gets stripped by a concurrent write.

View original on GitHub ↗

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