[BUG] ~/.claude.json.tmp.<pid>.<hash> atomic-write leftovers are never reclaimed by the retention sweep

Status Open
Reported on v2.1.220
Maintainer reply ✓ Yes — bcherny
Activity 3 comments · opened Aug 18, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

What happened

Cleaning up a full disk I found four stale temp files sitting next to ~/.claude.json:

/root/.claude.json.tmp.2942327.410b0df6512e   53.1K
/root/.claude.json.tmp.4105867.e6ce7f85d31b   66.1K
/root/.claude.json.tmp.469437.07ff79270095    66.4K
/root/.claude.json.tmp.618010.1da311f03c01    66.6K

~/.claude.json itself is 67.8K, so each of these is a near-complete copy. None of the four PIDs were alive:

$ for f in /root/.claude.json.tmp.*; do
    pid=$(echo "$f" | sed -E 's/.*\.tmp\.([0-9]+)\..*/\1/')
    kill -0 "$pid" 2>/dev/null && echo "$pid live" || echo "$pid dead"
  done
2942327 dead
4105867 dead
469437 dead
618010 dead

The <pid>.<hash> naming reads like a write-then-rename, so these look like the losing side of an atomic write when the process died before the rename landed.

Expected

The startup retention sweep reclaims them, or the next successful write of ~/.claude.json cleans up siblings whose PID is gone.

Actual

They persist indefinitely. The sweep is clearly running and working on everything else it owns — .last-cleanup was stamped today, and 0 of 570 transcripts under ~/.claude/projects are older than the 30-day default — but these files sit outside it. They're in $HOME, not in ~/.claude/ and not in a tmpdir, so nothing enumerates them.

Why it's worth fixing

The size is small per file, but it's unbounded and grows with every crash or kill, and it scales with ~/.claude.json itself. On a box with several concurrent sessions and the occasional OOM kill this accumulates quietly. kill -0 on the embedded PID makes it a cheap, safe check — a dead PID means nobody is going to rename that file.

Environment

Claude Code 2.1.220, Ubuntu 22.04.5, kernel 5.15.0-186, single ext4 root.

View original on GitHub ↗

3 Comments

bcherny collaborator · 1 month ago

Confirmed / reproduced on 2.1.234 (Linux): in a fresh home I planted two ~/.claude.json.tmp.<pid>.<hex> files (dead PIDs, mtime 60 days old) next to ~/.claude.json, then ran a session long enough for the retention sweep to complete — ~/.claude/.last-cleanup was freshly stamped, and old files the sweep does own were reclaimed in the same run. Both .claude.json.tmp.* orphans survived untouched. As you observed, they sit in the home directory root rather than under ~/.claude/, and nothing in the sweep enumerates that location, so they accumulate until removed by hand.

🤖 Generated with Claude Code

---
_Generated by Claude Code_

Roorn · 1 month ago

Adding a datapoint from #89565 (duplicate of this issue — closing it in favor of this thread): on a headless server that restarts the CLI nightly for auto-updates, these orphans appear on a schedule. Five of the six files I found matched the exact restart timestamps of the nightly update (03:32–03:45 over several nights), the sixth matched a watchdog-triggered restart during a network outage. So any setup with scheduled restarts accumulates them steadily: the process is killed between the tmp-write and the rename, and — as confirmed above — the startup sweep never reclaims them.

Workaround we now run in the nightly update script: delete ~/.claude.json.tmp.* older than one day whose embedded PID is no longer alive.

kenta-0420 · 20 days ago

Another data point, from Windows 11 (10.0.26200), Claude Code 2.1.281. On a machine that runs several Claude Code sessions in parallel (plus subagents), 364 ~/.claude.json.tmp.<pid>.<hash> files had built up in the user profile directory. By creation month, the profile's top-level entries were: 119 from August and 252 from September, most of them these temp files.

This also has an effect outside Claude Code. On Windows, OpenAI's Codex CLI sandbox lists every top-level entry of %USERPROFILE% into a payload that it passes on the command line (openai/codex#38985). The leftover temp files alone pushed that payload past the 32,767-character CreateProcess limit, and every sandboxed command failed with os error 206. Deleting the stale .claude.json.tmp.* files (450 → 75 top-level entries) fixed it right away.

+1 to having the startup sweep (or the next successful write) remove .claude.json.tmp.* siblings whose PID is no longer alive. Writing the temp file inside ~/.claude/ instead of the home root would also keep the leftovers out of the profile top level.