Bash tool: runaway child processes are memory-uncapped and outlive their parent shell's timeout — parallel sub-agents' orphaned pytest children exhausted 128 GB RAM and caused a macOS watchdog kernel panic
Preflight
Searched existing issues. This is related to, but distinct from, a cluster of unresolved
reports (see Related issues); the closest match (#22373) was closed as stale without a
fix, and none cover the orphaned-child-process-group angle below.
Summary
The Bash tool provides no containment for a child process that misbehaves on memory:
- there is no per-call or cumulative memory cap on Bash children, and
- when a Bash call is terminated (its timeout firing as SIGURG / exit 144, per #34138),
the spawned child is not reliably killed with its process group — it is orphaned
and keeps running and allocating.
Under a parallel multi-agent workflow this compounds. Six sub-agents each repeatedly ranpython -m pytest; a buggy test allocated without bound; orphaned interpreters accumulated
to 20–26 concurrent processes (one peaking at 144.9 GB, summing >500 GB), exhausted
128 GB of RAM, drove the macOS VM compressor to 100% of its segment limit (71 swapfiles),
and the hardware watchdog rebooted the machine
(panic: watchdog timeout: no checkins from watchdogd in 94 seconds).
The application bug that triggered the allocation was mine (already fixed). This report is
about Claude Code's absent containment, which turned a hung test into a host kernel panic.
Environment
- Claude Code via the VS Code extension over SSH remote
- macOS 26 (Darwin 25.5.0, build 25F80), Apple Silicon (Mac17,6), 128 GB RAM
- Multi-agent
parallel()workflow, 6-wide fan-out (~18 agents total) - Child workload:
.venv/bin/python -m pytest(Python 3.14, Homebrew)
Evidence
- Two
panic-fullreports:100% of segments limit (BAD) with 71 swapfiles,
largestZone = compressor_segment.
- Jetsam
.ips: a singlePythonat 144.86 GB / 803 s CPU; a later snapshot with
26 concurrent Python summing ~504 GB — all in one jetsam coalition alongside
claude / node / zsh.
- Session transcript: ~199 python-running Bash calls; exit code 144 (SIGURG) observed
= the Bash timeout fired (the signature from #34138) — yet a Python process ran for
803 s, far beyond the ~73 s ceiling, which is only explicable if the child was
orphaned after its parent shell was timed out/killed.
- Agents repeatedly hit
command not found: timeout(macOS ships notimeout) and fell
back to subprocess.run(timeout=) wrappers — i.e. no working timeout was available.
Why existing mechanisms don't cover this
BASH_DEFAULT_TIMEOUT_MS/BASH_MAX_TIMEOUT_MSare reported non-functional
(#34138, #3964, #26660). Even when working, a wall-clock timeout does not stop a fast
allocator from OOMing the host within the timeout window.
- A timeout that kills only the shell — not the child process group — leaves orphaned
runaways (cf. #19433, orphan children on /exit).
- There is no memory-aware limit or backpressure across concurrent sub-agents
(cf. #45880: MCP node processes, the same "no memory cap → macOS watchdog panic" family).
Related issues (recurring, unresolved failure mode)
- #22373 — Python multiprocessing/joblib → system freeze on Apple Silicon (closest; closed stale)
- #45880 — concurrent sessions spawn memory-uncapped processes → macOS watchdog kernel panic (closed stale)
- #34138 / #3964 / #26660 —
BASH_*_TIMEOUT_MSnon-functional (closed) - #19433 —
/exitleaves orphan child processes (closed) - #11315 — the
claudeprocess itself reaching 129 GB (open; different root cause, same symptom)
Reproduction (no private code needed)
- Ask Claude Code to run a foreground Bash command that allocates without bound:
````
python3 -c "x=[]
while True: x.append(bytearray(10_000_000))"
- Observe: no memory cap; RSS climbs until the OS swaps/kills.
- Let the Bash call time out (or cancel it) and check
pgrep -f python— the child may
persist and keep allocating.
- Do this inside a
parallel()/ multi-subagent workflow and the effect stacks with no
throttling, no refusal-to-spawn, and no kill on sub-agent completion.
Expected
- Optional memory cap on Bash children (per-call and/or cumulative), with backpressure
on sub-agent dispatch when exceeded.
- On timeout / completion / cancel, kill the child's full process group so nothing is
orphaned.
- On platforms without
timeout, an internal watchdog — and a functional
BASH_DEFAULT_TIMEOUT_MS.
- A runaway child should fail the tool call, not the host.
Caveats
- This Darwin build emits no per-process ppid records, so the process→sub-agent mapping
rests on jetsam-coalition membership + spawn-order adjacency + transcript correlation,
not a literal ppid chain. The orphaning is strongly indicated by the 803 s CPU ≫ the
~73 s timeout ceiling, not directly logged.
- The originating allocation bug was in my own test harness; this issue is specifically
about the containment gaps above.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗