Backgrounded (&) shell children survive a Bash-tool timeout and leak as PID-1 orphans, related to #45717

Status Open
Reported on v2.1.219
Maintainer reply None cached
Activity 0 comments · opened Jul 30, 2026

Title

Backgrounded (&) shell children survive a Bash-tool timeout and leak as PID-1 orphans, related to #45717

Description

Related to #45717 (closed "not planned"), which describes SIGTERM on Bash timeout over-killing (taking down the whole Claude Code process instead of just the child). This report is the inverse symptom of what looks like the same root cause: process-group scoping around Bash tool timeouts is not correct, and in this case it under-kills, leaking orphaned processes indefinitely.

Repro

  1. In a Bash tool call, background several CPU-bound children via shell job control, then rely on kill $BUSY (BUSY=$(jobs -p)) at the end of the same script to clean up:

``sh
for c in $(seq 8); do (while :; do :; done) & done
BUSY=$(jobs -p)
npm test ...
kill $BUSY 2>/dev/null
``

  1. Let the surrounding command run long enough to hit BASH_MAX_TIMEOUT_MS (default 600000ms / 10 min), so Claude Code kills the command before it reaches the kill $BUSY line.
  2. Observe: the zsh -c ... process for that tool call is killed (matches the "Exit code 143 — Command timed out after 10m 0s" result shown to the model), but the already-forked while :; do :; done children are not — they get reparented to PID 1 and continue running indefinitely, pinning CPU cores.

Impact observed

A single Claude Code background session (spawned via the Claude Desktop app, not an interactive terminal) repeated this pattern several times over ~2 hours of iterative flaky-test debugging. By the time it was caught, ~30+ orphaned while :; do :; done processes were running, driving the 1-minute load average to ~700+ on an otherwise idle machine and pinning all cores (audible fan noise was the first symptom noticed).

Expected behavior

When the Bash tool times out a command, it should kill the command's entire process group (e.g. spawn via setsid/new process group and signal the group, per the fix proposed in #45717), not just the immediate PID — so that any &-backgrounded children die along with their parent shell.

Workaround in use

Avoiding shell-level & backgrounding inside synchronous Bash tool calls entirely, in favor of the tool's own run_in_background: true, which is tracked and cleaned up by Claude Code's own task lifecycle regardless of timeout behavior.

Environment

  • Claude Code 2.1.219
  • macOS (Darwin), session spawned via Claude Desktop app background task (not an interactive terminal)

View original on GitHub ↗