settings.json overwritten with valid {"env": {}} — stale in-memory config re-persisted (not truncation) with many concurrent sessions on Windows
Environment
- Claude Code v2.1.207, Windows 11 Pro 10.0.26200, PowerShell
- ~15 concurrent interactive sessions (terminal workspace with per-project tabs)
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1enabled in~/.claude/settings.jsonenv
What happened
~/.claude/settings.json (~1.9 KB: 12 enabledPlugins, extraKnownMarketplaces, permissions, model, theme, autoMode, env) was replaced by exactly:
{
"env": {}
}
15 bytes, valid JSON, complete write — not a truncated/corrupted file. All user config (plugins, marketplaces, permissions, model, autoMode rules) was silently lost.
Forensic timeline (local time, 2026-07-11)
| Time | Event | Evidence |
|---|---|---|
| 21:38–21:43 | 15 sessions launched within ~5 min (workspace tabs), all reading settings.json at startup | 15 ~/.claude/teams/session-* dirs created seconds apart |
| 23:13 | /model executed in one session → CLI saved settings.json (healthy, env key reordered to top = CLI serializer fingerprint) | history.jsonl entry |
| 23:18:54 | Last conversation activity across ALL sessions | transcript timestamps |
| 23:27:06 | settings.json mtime = wipe moment. No conversation turn active anywhere | file mtime + zero transcript lines 23:20–23:29 across all projects |
Why this is a distinct variant from the known .claude.json truncation family
The known reports (#28824, #28842, #28847, #28992, #29036, #29198, #29217, #28922, #40226) describe truncated/invalid JSON from broken atomic rename on Windows. This case is different:
- The written content is complete, valid JSON with the same key-ordering fingerprint as the CLI's own serializer — i.e. a successful, intentional save of a nearly-empty config object.
- It happened while no turn was running → a lifecycle write (session exit / housekeeping), not a tool action.
- Most plausible mechanism: one of the mass-launched sessions read an empty/partial view of settings.json during another process's non-atomic write window (e.g. at the 21:38 startup stampede or the 23:13
/modelsave), kept that empty config in memory for ~2h, then re-persisted it on exit when the user closed the workspace (~23:27), clobbering the good file.
So even if the write path is made atomic, the read-merge-write cycle is still unsafe: a stale read + later full-object save silently destroys config written by other sessions in between.
Suggestions
- Atomic write (temp + fsync + rename with retry on EPERM) — covers the truncation family.
- Re-read + merge (or compare mtime/hash) before any full-object save, especially saves triggered at session exit — covers this stale-write variant.
- Treat an unexpectedly empty/default parse of an existing settings.json as suspicious: back it up before ever writing over it, or refuse to persist a config that lost keys it didn't touch.
Impact
Silent loss of all user configuration; with enabledPlugins/extraKnownMarketplaces gone, every subsequent session starts degraded and the user may not notice for hours. (Recovered here only because the full content happened to be readable in a live session's context.)