[BUG] Session scratchpads under /tmp/claude-<uid> never garbage-collected after exit — 8.8 GB of dead sessions resident in RAM on tmpfs, drives swap thrashing
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Each session creates a scratchpad directory under /tmp/claude-<uid>/<project-slug>/<session-id>/. These are never removed: no cleanup on graceful exit, no startup sweep for crash leftovers, no size/age cap. On distros where /tmp is tmpfs (RAM-backed — the Ubuntu default), every exited session's scratchpad permanently consumes RAM, and under memory pressure those dead pages get swapped out, directly causing swap thrashing.
Measured on one machine (2026-07-07, Ubuntu 26.04, 61G RAM, /tmp = 31G tmpfs):
/tmpheld 24 GB, of which 22 GB was/tmp/claude-1000/.- Classified every session dir by liveness — a dir is dead iff no running process's environment carries its
CLAUDE_CODE_SESSION_ID(children inherit it, so scanning/proc/*/environis an exact signal): - 95 dead sessions = 8.8 GB (leak)
- 24 live sessions = 13.3 GB (legitimately in use)
- Deleting only the dead dirs: RAM used 42→38 Gi, swap 7.7 Gi (96% full) → 2.8 Gi (35%). Swap dropped more than RAM — the dead scratchpads had been paged out and were the direct cause of system-wide thrashing.
Two important notes for whoever fixes this:
- mtime is NOT a safe deletion heuristic. The single largest scratchpad (9.7 GB, mtime 3 days stale) belonged to a live session. Only the session-ID liveness check is safe.
- This is distinct from the (also stale-closed, never fixed) unbounded-growth-during-session family: #40108, #42125, #42388, #47771, #51814. Those are about size during a running session; this report is about dead sessions never being reaped at all. Related open issue for a different artifact with the same no-GC lifecycle: #8856 (
/tmp/claude-*-cwdtracking files).
Discovery condition: this only becomes visible to users who run many/long-lived sessions — short-session users never accumulate enough to notice, which is likely why it has survived. Suggest adding session-lifetime/accumulation as a test dimension.
What Should Happen?
A session's scratchpad should be reclaimed when the session ends:
- Remove the scratchpad dir on graceful exit.
- Startup GC for crash/
killleftovers: sweep/tmp/claude-<uid>, delete any session dir whose ID is not referenced by any live process'sCLAUDE_CODE_SESSION_ID(exact and concurrent-session-safe). - Size/age cap as a backstop.
Error Messages/Logs
$ df -h -t tmpfs | grep -E '/tmp$'
tmpfs 31G 24G 7.3G 77% /tmp
$ du -sh /tmp/claude-1000
22G /tmp/claude-1000
$ free -h # before cleanup
Mem: 61Gi used 42Gi ... Swap: 8.0Gi used 7.7Gi
$ # liveness-checked purge of 95 dead session dirs (8.8 GB) — live sessions untouched
$ free -h # after cleanup
Mem: 61Gi used 38Gi ... Swap: 8.0Gi used 2.8Gi
Steps to Reproduce
- On a machine where
/tmpis tmpfs, run Claude Code sessions over several days (start and exit many sessions; heavy tool use grows the scratchpads). du -sh /tmp/claude-$(id -u)— observe monotonic growth; exited sessions' dirs are still present.- Build the live-session set:
for e in /proc/[0-9]*/environ; do tr '\0' '\n' < "$e" 2>/dev/null; done | grep -oE 'CLAUDE_CODE_SESSION_ID=\S+'and compare against the dirs on disk — every session dir not in the live set is leaked.
Environment
- Claude Code 2.1.202 (native install), also observed under 2.1.198–2.1.201
- Ubuntu 26.04 LTS, kernel 7.0.0-22-generic, x86_64
/tmp= tmpfs 31G (default), 61G RAM
Investigated and measured with Claude Code itself; full methodology available on request.
3 Comments
Confirming this on Arch Linux (CachyOS), Claude Code 2.1.215, multiple concurrent CLI sessions.
Just checked
/tmp/claude-1000(tmpfs, 8.8G): 1603 session scratchpad directories on disk, but only 12claudeCLI processes actually running at the time of checking. So the overwhelming majority of these directories belong to sessions that exited (crashed,/compact, normal exit) long ago and were never cleaned up — some dating back multiple days.Since
/tmpis tmpfs here, every one of those dead directories is resident RAM, not disk. Combined with running several concurrent sessions, this was a real contributor to swap thrashing on a 17GB-RAM machine — had to add/grow a disk swapfile as a workaround. Restarting the Claude Desktop app (which also crashed once during this) doesn't clean up leftover scratchpad directories from prior sessions.Would be great to see either (a) cleanup on clean session exit, or (b) a periodic reaper for scratchpad dirs whose owning PID is no longer alive.
Tested this on 2.1.233 on Linux. Partially confirmed — with one correction to the "never" part.
What I verified:
/tmp/claude-<uid>/<project>/<session>/are indeed left behind after sessions exit (bothclaude -pand interactive/exit). Nothing cleans them at exit time./tmpscratch directory. The scratch dir's lifetime is tied to the session transcript'scleanupPeriodDaysretention (default 30 days). The sweep runs a few seconds into a session, at most once per 24h, and waits ~10 minutes for idle in interactive sessions — so quick spot-checks can miss it.So: up to 30 days of scratch data on a tmpfs
/tmpis currently by design (loweringcleanupPeriodDaysshortens it), but the orphaned-directory leak is a genuine bug, and none of this/tmpcleanup is documented yet. In the meantime it's safe to manually delete/tmp/claude-<uid>subdirectories that don't belong to a running session.🤖 Generated with Claude Code
Please also consider the comment in the other issue.
It's not only about cleaning up, but also advising agents to use
/tmp/for scratchpad work in the first place.An agent trying something out with an 8GB blob will do the experiment in RAM by default.
That memory is automatically lost during the experiment and that surely should _not_ be the case in the first place.
Scratchpad directories should _not_ be in
/tmp/but in/var/tmp.Also the misleading doc should be changed.