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

Open 💬 1 comment Opened Jun 15, 2026 by nirat25

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:

  1. there is no per-call or cumulative memory cap on Bash children, and
  2. 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 ran
python -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-full reports: 100% of segments limit (BAD) with 71 swapfiles,

largestZone = compressor_segment.

  • Jetsam .ips: a single Python at 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 no timeout) 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_MS are 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 / #26660BASH_*_TIMEOUT_MS non-functional (closed)
  • #19433/exit leaves orphan child processes (closed)
  • #11315 — the claude process itself reaching 129 GB (open; different root cause, same symptom)

Reproduction (no private code needed)

  1. Ask Claude Code to run a foreground Bash command that allocates without bound:

``
python3 -c "x=[]
while True: x.append(bytearray(10_000_000))"
``

  1. Observe: no memory cap; RSS climbs until the OS swaps/kills.
  2. Let the Bash call time out (or cancel it) and check pgrep -f python — the child may

persist and keep allocating.

  1. 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.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗