[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

Open 💬 0 comments Opened Jul 7, 2026 by asuramaya

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 ↗