Atomic-write leaves `.tmp.<pid>.<hex>` residuals (Edit/Write + plugin LSP), and plugin `.in_use` lock files leak on process exit — Windows (follow-up to #60146)
Summary
Follow-up to #60146, which was auto-closed by the stale bot on 2026-06-16 without ever being triaged or answered. The behavior still reproduces, and I've characterized a second, related leak since the original report. Filing fresh per the stale bot's guidance ("open a new issue if this is still relevant"), consolidating both.
There are two related residual leaks under ~/.claude on Windows, both rooted in atomic-write / cleanup-on-exit:
Edit/Writeatomic-write residuals —<filename>.tmp.<pid>.<hex>files remain after the tool call returns success (the original #60146 report).- Plugin LSP lock-directory leak — the bundled
pyright-lsp/typescript-lspreference-count lock dirs (.in_use/) both (a) contain the same.tmp.<pid>.<hex>atomic-write residual, and (b) leak their settled lock files on process exit.
Current census (Windows 11, Git Bash harness, 2026-06-16)
Strict match to the two original patterns (.tmp.<pid>.<hex> and .tmp.<hex>):
| Location | .tmp.<pid>.<hex> residuals |
|---|---|
| plans/ | 39 |
| projects/<hash>/memory/ | 14 |
| .credentials.json.tmp.<hex> (root) | 2 |
| plugin LSP .in_use/ dirs | 286 |
| total | 347 |
Plus 504 settled lock files in the plugin LSP lock dirs (252 in each of pyright-lsp / typescript-lsp .in_use/). The original report observed 412 atomic-write residuals; they accumulate continuously.
Leak 1 — Edit/Write atomic-write residuals (original #60146)
Editing/creating a file via Edit/Write leaves a <filename>.tmp.<pid>.<hex> residual in the directory after the call returns success. Two suffix shapes observed:
<filename>.tmp.<pid>.<hex>— e.g.MEMORY.md.tmp.133200.e9386df7a3af(most files)<filename>.tmp.<hex>— e.g..credentials.json.tmp.<hex>(credentials file only, no PID segment)
Repro:
Editany file (e.g.~/.claude/CLAUDE.md).find <dir> -name '*.tmp.*'→ residual remains, mtime ~seconds before the final file, never cleaned.- Across sessions these accumulate (one extended setup reached 412). Full original detail in #60146.
Leak 2 — plugin LSP .in_use/ lock directories (new)
Path: ~/.claude/plugins/cache/claude-plugins-official/{pyright-lsp,typescript-lsp}/<ver>/.in_use/
Two compounding problems:
- Same atomic-write residual as Leak 1: 286 of the 347 residuals above live inside these two
.in_use/dirs — the lock mechanism uses the same atomic write and leaves the same.tmp.<pid>.<hex>residual. - Cleanup-on-exit leak of settled lock files: the lock files (
{"pid","procStart"}JSON, named by PID) are not removed on process exit and accumulate unbounded. A liveness audit of one 6,874-file snapshot across 12 plugin dirs — cross-checking each lock's(pid, procStart)pair against running processes — found 0 active / 100% leaked (every PID-number match was coincidental reuse;procStartdiffered). After deleting 6,856, they re-accumulated to 504 within ~17 hours, indicating an active leak. The live LSP servers do not appear to hold these as live locks.
Environment
- Claude Code CLI: bundled (
...\AppData\Roaming\Claude\claude-code\<version>\claude.exe); originally observed2.1.138, still reproducing 2026-06-16. - OS: Windows 11, Git Bash shell harness.
- Affected dirs:
~/.claude/projects/<hash>/memory/,plans/,rules/,skills/,scripts/, root.credentials.json.tmp.*, andplugins/cache/.../{pyright,typescript}-lsp/<ver>/.in_use/.
Additional environment finding — PowerShell Remove-Item silently blocked
Cleaning these residuals via PowerShell Remove-Item is silently blocked (no error; the count is unchanged afterward), apparently by a sandbox/file guard. Deletion only succeeds via Git Bash find ... -delete. Flagging for anyone scripting a Windows cleanup hook.
Workaround (manual, per session)
find ~/.claude -name '*.tmp.*' \
\( -regextype posix-extended -regex '.*\.tmp\.[0-9]+\.[a-f0-9]+$' -o -regex '.*\.tmp\.[a-f0-9]+$' \) \
-mmin +5 -delete 2>/dev/null
# plugin lock leak: separately delete settled lock files under .in_use/ older than a few minutes
Questions
- Intent — is
.tmp.<pid>.<hex>an intentional atomic-rename artifact, or a cleanup miss after rename? - Atomicity — is
tmp → finalrename guaranteed atomic, i.e. safe to delete residuals matching the patterns after the tool call returns? - Cleanup timing — for a
PostToolUsehook, isfind ... -mmin +5 -deletesafe, or could it race an in-progress transaction? - Schema — a 2026-04-24 observation showed suffix
<timestamp_ms>; 2026-05-18 onward shows<hex>. Intentional change? Is the hex a UUID, random, or other? .in_uselock lifecycle — should plugin lock files be removed on process exit? If so, Leak 2 is a bug; if not, what is the intended GC path?
Impact
Disk: small per file (~5–100 KB) but hundreds–thousands across long-running setups. Clutters ls / find. Requires per-session manual cleanup if not automated. The plugin .in_use leak grows fastest (6,874 in extended use; 504 within ~17h of a clean).
Happy to provide a full listing, the liveness-audit script, or a minimal repro. Thanks!