[BUG] Bash tool leaves orphaned ugrep pinning 12 cores for 22 min after tool call completes; subagent ran filesystem-wide search outside project scope
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Environment
- Claude Code 2.1.208
- Ubuntu, running via VS Code extension
- Intel i7-13700H, 20 logical cores
- Project: 72 MB, 2,724 files
- Model: opus[1m]
What happened
An orchestrating agent spawned an Explore subagent with a prompt instructing it to
search "the WHOLE repo and the machine" and to look for files "anywhere on disk".
The subagent ran (patterns redacted):
grep -rln 'pattern1\|pattern2' / 2>/dev/null | head
find / -iname 'keyword' 2>/dev/null | grep -v -E '/proc/|/sys/' | head -40
Claude Code's shell snapshot shadows grep with a bundled ugrep
(snapshot-bash-*.sh line 85: "Shadow find/grep with embedded bfs/ugrep"),
rewriting the call to:
ugrep -G --ignore-files --hidden -I --exclude-dir=.git --exclude-dir=.svn
--exclude-dir=.hg --exclude-dir=.bzr --exclude-dir=.jj --exclude-dir=.sl
ugrep spawns one worker thread per core. Rooted at /, this traversed mounted NTFS
volumes entirely unrelated to the project.
Defect 1 (primary): orphaned process outlives the tool call
Timeline (IST):
- ~20:42 ugrep starts
- 20:45:44 subagent transcript last write — tool call returned, subagent moved on
to a narrower scoped retry and continued reading files
- 21:07 process still running; ps showed ELAPSED 1491s, 12 threads at ~100%
- ~21:08 stopped only by manually killing the parent claude process
The tool call completed but the spawned process tree was never terminated. Nothing
in the UI indicated a background process was still running.
Possible mechanism: the command ends in | head. When head exits, the pipe closes
and the tool call returns — but ugrep's worker threads appear not to terminate on
the broken pipe, continuing to traverse / long after the parent finished.
Defect 2: unbounded search scope
The orchestrating agent wrote a filesystem-wide search into a subagent brief. No
path bound, no confirmation prompt, no thread cap. Reading / can pull file contents
from outside the project into model context — on this machine that included mounted
Windows and external volumes.
Impact
- 12 cores at 100% for ~25 min (~5 CPU-hours), load average 14.59
- 4.7 GB read
- 2>/dev/null suppressed every error from the traversal
- Diagnosed only via htop + pstree; no cancel affordance in the UI
What Should Happen?
Expected
- Tool call termination should kill the spawned process group.
- Searches should default-bound to the project root; filesystem-wide scope should
require explicit user confirmation.
- Long-running tool calls should be visible and cancellable.
Repro sketch
Ask a subagent to locate a file "anywhere on disk" on a machine with a large
mounted volume, then observe whether the ugrep survives the tool call's return.
Error Messages/Logs
Steps to Reproduce
Steps to reproduce
Setup: build a tree large enough that traversal takes a while
mkdir -p /tmp/orphan-repro && cd /tmp/orphan-repro
mkdir -p a && echo 'MATCHME' > a/000-first.txt
python3 -c "
import os
for i in range(200000):
d = f'tree/{i//1000:03d}'
os.makedirs(d, exist_ok=True)
open(f'{d}/f{i}.txt','w').write('x'*2000)
"
Result: ~200k files, ~400 MB, with one early match.
Reproduce
- Start Claude Code from any project directory.
- Ask it to run:
grep -rln 'MATCHME' /tmp/orphan-repro | head -1
(Or ask in natural language: "find which files under /tmp/orphan-repro
contain MATCHME, just the first hit.")
- Note the wall-clock time when the tool call returns in the UI — it returns
almost immediately, because head -1 exits on the first match and closes
the pipe.
- In a SEPARATE terminal, immediately run:
watch -n1 'ps -eo pid,etimes,nlwp,pcpu,args -C ugrep'
Observed
ugrep continues running after the tool call has returned, with nlwp equal to
the core count and pcpu near 100% per thread. It keeps traversing the tree
until it finishes on its own. Killing the Claude Code session does not stop
it; the process must be killed manually.
In the original incident this ran 22+ minutes past the parent's completion.
Expected
The process group spawned by the Bash tool should be terminated when the tool
call completes or times out.
Control
Running the same command with the shim bypassed completes and exits cleanly:
/usr/bin/grep -rln 'MATCHME' /tmp/orphan-repro | head -1
This confirms the behavior comes from the bundled ugrep invoked via the shell
snapshot shim (snapshot-bash-
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.208
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
VS Code integrated terminal
Additional Information
i was using the claude code in plan mode to discuss the slice details. If you ask i can share the chat discussion which captures logs, timing and other stuff.