.claude.json corrupts on Windows with multiple sessions — silent config loss

Resolved 💬 1 comment Opened Feb 26, 2026 by luongjames8 Closed Feb 26, 2026

Bug Summary

~/.claude.json corrupts when multiple Claude Code sessions run concurrently on the same machine. The recovery process silently drops critical config sections (e.g., mcpServers), requiring manual backup restoration.

Root Cause

Claude Code writes to ~/.claude.json on every session (updating toolUsage, clientDataCache.timestamp, changelogLastFetched, etc.). With multiple sessions open, concurrent writes race and one truncates the file mid-write, producing invalid JSON (Unexpected EOF).

Current Recovery Behavior (Broken)

When corruption is detected on startup:

  1. Corrupted file is backed up to ~/.claude/backups/.claude.json.corrupted.<timestamp>
  2. A previous backup is offered for restoration
  3. If no restoration happens, a minimal new file is created with only basic keys

Problem: The minimal file drops mcpServers and other user-configured sections entirely. No merge is attempted. The user loses their MCP server configuration (potentially 10+ servers) silently.

Observed Symptoms

  • Error on startup: Claude configuration file at ~/.claude.json is corrupted: JSON Parse error: Unexpected EOF
  • userID changes on every session write (4+ different IDs in 7 minutes)
  • mcpServers section (11 servers) completely gone after recovery
  • Multiple .corrupted.* and .backup.* files accumulate in ~/.claude/backups/

Proposed Fix (Two Parts)

Part 1: Prevent Corruption — Atomic Writes

Replace direct fs.writeFileSync('~/.claude.json', data) with atomic write pattern:

const tmpPath = configPath + '.tmp.' + process.pid;
fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
fs.renameSync(tmpPath, configPath);  // atomic on same filesystem

This ensures the file is either fully written or not modified at all. rename() is atomic on POSIX and NTFS (Windows).

Files to change: Search codebase for all writeFileSync or writeFile calls targeting .claude.json or the config path.

Part 2: Smarter Recovery — Merge Instead of Replace

When corruption is detected and a backup exists, the recovery should:

  1. Parse the backup file
  2. Parse/create the new minimal config
  3. Merge user-configured sections from the backup — specifically:
  • mcpServers (critical — user's MCP server definitions)
  • projects (project-specific settings)
  • Any other user-configured keys
  1. Use the current session's runtime values for volatile keys:
  • userID, toolUsage, clientDataCache, changelogLastFetched, etc.
  1. Write the merged result (using atomic write from Part 1)
// Pseudocode for recovery merge
const backup = JSON.parse(fs.readFileSync(backupPath));
const fresh = createDefaultConfig();

// Preserve user config from backup
const USER_CONFIG_KEYS = ['mcpServers', 'projects'];
for (const key of USER_CONFIG_KEYS) {
  if (backup[key] && Object.keys(backup[key]).length > 0) {
    fresh[key] = backup[key];
  }
}

atomicWrite(configPath, fresh);

Environment

  • OS: Windows 11 Home 10.0.26200
  • Claude Code: 2.1.59
  • Shell: Git Bash
  • Trigger: Multiple concurrent sessions (VS Code terminals + standalone)

Reproduction

  1. Configure 5+ MCP servers in ~/.claude.json under mcpServers
  2. Open 3+ Claude Code sessions simultaneously
  3. Use them actively for a few minutes (triggers config writes)
  4. Close and reopen a session — observe corruption error
  5. Check ~/.claude.jsonmcpServers is gone

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗