.claude.json corruption with concurrent sessions (v2.1.59, Windows)

Resolved 💬 4 comments Opened Feb 26, 2026 by supraforge-mueller Closed Feb 26, 2026

Bug Description

.claude.json (user config file) gets repeatedly corrupted when running multiple Claude Code sessions in parallel on Windows. This is a regression — parallel sessions worked reliably for months prior to recent versions.

Environment

  • Claude Code version: 2.1.59
  • OS: Windows 11 Home 10.0.26200
  • Node.js: v24.11.1
  • Shell: Git Bash
  • Previous working versions: 2.1.41 through 2.1.47 (from npm cache history)

Reproduction Steps

  1. Open 2+ Claude Code terminal sessions simultaneously (e.g., 4 sessions in different project directories)
  2. Work normally in each session (tool calls, file reads, etc.)
  3. After a few minutes, one or more sessions report:

``
Claude configuration file at C:\Users\<user>\.claude.json is corrupted: Unexpected end of JSON input
``

  1. The file is backed up and reset to near-empty defaults
  2. This repeats in a loop — 29 "corrupted" backup files were generated in a single session today

Root Cause Analysis

I traced the issue through the minified CLI source (cli.js). Here's what happens:

1. Diff-only write strategy

The config save functions (_Gq with lock, wGq without lock) write only fields that differ from defaults, not the full config:

// Both _Gq and wGq do this filtering:
let diff = Object.fromEntries(
  Object.entries(config).filter(([key, val]) => JSON.stringify(val) !== JSON.stringify(defaults[key]))
);
writeFile(path, JSON.stringify(diff, null, 2));

2. Lock-based save fails, fallback has no merge

The save flow in W8 (the main config save function):

function W8(updater) {
  try {
    _Gq(configPath, defaults, (current) => { // Lock-based: read-modify-write
      return updater(current);
    });
  } catch(err) {
    // FALLBACK: No lock, no read-modify-write!
    let current = readConfig(configPath, defaults);
    let updated = updater(current);
    wGq(configPath, updated, defaults); // Writes diff directly, no lock
  }
}

When the lockfile mechanism fails (common with 4 parallel processes on Windows), the fallback path wGq writes without coordination.

3. Sessions overwrite each other's data

Timeline:

  1. Session A writes {fieldA: val, fieldB: val} (its diff from defaults)
  2. Session B writes {fieldX: val, fieldY: val} (its diff from defaults)
  3. Session B completely overwrites Session A's data — there's no merge
  4. Next read by any session finds missing expected fields → "corrupted"

Evidence

| File | Size | Top-level keys | Notes |
|------|------|---------------|-------|
| .claude.json.backup (Jan 29, pre-regression) | 8,908 bytes | 38 keys | Full config, everything intact |
| Current .claude.json | ~300-700 bytes | 3-5 keys | Fragment — only one session's diff |
| Various "corrupted" backups | 77-4,938 bytes | 1-15 keys | Different sessions' partial writes |

Different userID values appear across "corrupted" files, confirming different sessions are overwriting each other.

The "corrupted" files are actually valid JSON — they just contain a subset of keys that another session doesn't recognize as complete.

4. The Fe atomic write function

The low-level write (Fe) does use a temp-file + rename strategy:

function Fe(path, data, opts) {
  let tmpFile = `${path}.tmp.${process.pid}.${Date.now()}`;
  writeFileSync(tmpFile, data);
  renameSync(tmpFile, path); // "atomic" on POSIX, NOT guaranteed on Windows NTFS
}

However, renameSync is not atomic on Windows when another process has the target file open. This can cause additional corruption beyond the logical race condition.

Expected Behavior

Multiple parallel Claude Code sessions should be able to coexist without corrupting each other's config, as they did in versions ≤2.1.47.

Suggested Fixes

  1. Full config write, not diff-only: The wGq fallback should write the complete config object, not just the diff from defaults. This prevents data loss when one session overwrites another's partial state.
  1. Read-merge-write in fallback path: Even without a lock, wGq should read the current file, merge in changes, then write — reducing the window for data loss.
  1. Retry lock acquisition: Instead of immediately falling back to lockless writes, retry the lock 2-3 times with short backoff.
  1. Windows-specific lockfile handling: The proper-lockfile (or equivalent) library may need Windows-specific configuration. Consider using flock-style locks or Windows LockFile API.
  1. Corruption detection improvement: The current detection flags valid-but-incomplete JSON as "corrupted". Consider a schema validation or checksum approach that can merge partial configs rather than resetting to defaults.

Workaround

  • Run only one Claude Code session at a time
  • Or downgrade to 2.1.47: npm i -g @anthropic-ai/claude-code@2.1.47

---

Filed from Claude Code session analysis on Windows 11 with 4 concurrent claude.exe processes.

View original on GitHub ↗

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