Bash tool timeout leaks pipeline children: bundled grep escaped kill, ran 11.5h at 1199% CPU reparented to systemd --user
Summary
When a Bash tool call hits its timeout, Claude Code kills the shell but not the full process group. A long-running child of the pipeline (in this case Claude Code's own bundled grep) escapes the kill, gets reparented to systemd --user, and runs unowned — in my case pinning ~12 cores at 1199% CPU for 11.5 hours (≈136 CPU-hours) until I found and killed it manually.
Environment
- Claude Code 2.1.216 (Linux, installed under
~/.local/share/claude/versions/2.1.216) - Ubuntu, kernel 6.17.0-35-generic
- Session was running a multi-agent workflow (ultracode effort); the offending command came from a subagent
What happened
- A workflow subagent ran this Bash tool call (note the search path is
/— a bad scope choice by the agent, but that's the trigger, not the bug):
````
grep -rl "verified-execution marketplace\|proof-of-work\|Emerald Cloud Lab\|cross-credit\|Iris Publishers" / 2>/dev/null | grep -v "/proc/" | grep -v "\.git/" | grep -iE "\.md$|\.html$|\.json$|\.txt$" | grep -v "<project wiki path>" | head -30
- The command ran past the Bash tool timeout. The subagent's transcript shows it gave up on the result and moved on ("NOT available to me on disk").
- The shell was killed on timeout, but the first stage of the pipeline — Claude Code's bundled grep, still crawling
/— was not killed. It was reparented tosystemd --user(PID of parent became the user systemd instance) and kept running. - ~11.5 hours later it was still running:
````
USER PID %CPU %MEM TIME COMMAND
johnd 2513823 1199 1.1 8197:49 ugrep -G --ignore-files --hidden -I --exclude-dir=.git ... -rl <patterns> /
````
$ ls -l /proc/2513823/exe
/proc/2513823/exe -> /home/johnd/.local/share/claude/versions/2.1.216
$ ps -o ppid= -p 2513823
7250 # /usr/lib/systemd/systemd --user
Confirming it's the Claude-bundled binary (argv0 ugrep, exe → the versioned claude binary) with no living Claude Code ancestor.
Expected behavior
On Bash tool timeout (and on session exit), Claude Code should kill the entire process group / session of the spawned command (e.g. spawn with setsid/new pgid and send SIGKILL to -pgid after the grace period), so pipeline children can't outlive the tool call. A belt-and-braces sweep of surviving descendants on session shutdown would also have caught this.
Impact
- 136 CPU-hours wasted; machine degraded for an entire workday overnight
- Because the leaked process is Claude's own bundled grep with argv0
ugrep, it's non-obvious to users where it came from (ugrepisn't even on PATH —which ugrepreturns nothing, which makes it look like malware at first glance) - Any long-running child survives this way (
find /, a hung network call, etc.); a filesystem-root grep just makes it maximally visible
Repro sketch
- In a session, have the model run a Bash command guaranteed to exceed the tool timeout where the long-running stage is piped into another stage, e.g.
grep -rl pattern / 2>/dev/null | head -30 - Wait for the tool call to time out
ps -eo pid,ppid,comm,args | grep -i grep— the crawler is still running, reparented away from the claude process
🤖 Filed with Claude Code on behalf of the user