Background processes started by Bash tool are not cleaned up on session exit
Problem
When Claude Code starts background processes via the Bash tool (e.g., run_in_background, dev servers, builds, tests), those processes are not cleaned up when the session ends. The spawned Node processes get reparented to PID 1 (launchd on macOS) and continue running indefinitely.
Over time, this leads to significant memory consumption — orphaned next dev servers can grow to 8+ GB, and smaller orphaned processes (builds, codegen, PostCSS workers, test runners) accumulate and add up.
Current behavior
- Claude session starts a background process (e.g.,
PORT=3000 npm run dev) - Session ends (user exits, context exhausted, etc.)
- The shell wrapper exits, but child Node processes are reparented to PID 1
- Processes run indefinitely until manually killed
Why hooks don't solve this
- A
Stophook could kill processes on known ports, but that also kills user-started processes (false positives). - A PID-file approach works for specific process types (dev servers) but doesn't scale to every
npm run build,npx jest, codegen, etc. - Hooks can't inject env vars into Bash tool calls, so there's no way to tag Claude-spawned processes for selective cleanup.
Desired behavior
Claude Code should track all child processes spawned by the Bash tool during a session and clean them up (SIGTERM → SIGKILL) when the session exits. This should include the full process tree, not just the top-level PID.
Environment
- macOS (Darwin)
- Processes are primarily Node.js (Next.js dev servers, PostCSS workers, Jest, codegen)
- Multiple concurrent Claude sessions (tmux teammates, background agents) make port-based cleanup unreliable
This issue has 8 comments on GitHub. Read the full discussion on GitHub ↗