[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

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

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):

  • /tmp held 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/*/environ is 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:

  1. 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.
  2. 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-*-cwd tracking 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:

  1. Remove the scratchpad dir on graceful exit.
  2. Startup GC for crash/kill leftovers: sweep /tmp/claude-<uid>, delete any session dir whose ID is not referenced by any live process's CLAUDE_CODE_SESSION_ID (exact and concurrent-session-safe).
  3. 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

  1. On a machine where /tmp is tmpfs, run Claude Code sessions over several days (start and exit many sessions; heavy tool use grows the scratchpads).
  2. du -sh /tmp/claude-$(id -u) — observe monotonic growth; exited sessions' dirs are still present.
  3. 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.

View original on GitHub ↗

3 Comments

clemenssautter-ai · 1 month ago

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 12 claude CLI 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 /tmp is 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.

bcherny collaborator · 13 days ago

Tested this on 2.1.233 on Linux. Partially confirmed — with one correction to the "never" part.

What I verified:

  • Session scratch directories under /tmp/claude-<uid>/<project>/<session>/ are indeed left behind after sessions exit (both claude -p and interactive /exit). Nothing cleans them at exit time.
  • A startup sweep does exist, though: when I aged a session past 30 days, the next session removed both its transcript and its /tmp scratch directory. The scratch dir's lifetime is tied to the session transcript's cleanupPeriodDays retention (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.
  • The real bug I confirmed: scratch directories whose session transcript is already gone (sessions run without transcript persistence, transcripts deleted earlier or by older versions) are never removed — 40-day-old orphaned dirs survived the same sweep that cleaned up a control session. Your 8.8 GB is likely mostly this, and it won't be reclaimed automatically.

So: up to 30 days of scratch data on a tmpfs /tmp is currently by design (lowering cleanupPeriodDays shortens it), but the orphaned-directory leak is a genuine bug, and none of this /tmp cleanup 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

alexanderadam · 12 days ago

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.