[BUG] [Windows] TaskStop / background-task kill leaves Git Bash child processes running (orphaned grep, find, du, sleep for hours)
Environment
- Claude Code 2.1.296 (native
claude.exe), Windows 11 Pro 10.0.26200 - Shell for the Bash tool: Git for Windows 2.50.1 (
C:\Program Files\Git\bin\bash.exe, GNU bash 5.2.37 x86_64-pc-msys, MSYS runtime 3.6.3) - Reproduced inside a subagent and, with the same command, in the main thread; all traced cases in the wild were subagents.
- Related: #85200 (same symptom, data loss), #84464, #27273. This report adds the mechanism and a 5-second reproduction.
Summary
When a background Bash task is stopped — by the TaskStop tool, and by the harness itself when it terminates a
task — Claude Code kills the two bash.exe processes of the call and reports Successfully stopped task: <id>
(the output file ends with [killed]). The programs that shell had started (grep.exe, find.exe, du.exe,sleep.exe, head.exe … from Git\usr\bin) are not killed. They keep running with no parent and no owner
until they finish on their own, which for a recursive search can be hours.
The same sequence with the PowerShell tool and a native child (ping) kills the child correctly.
Why it happens (observed, not read from source)
A Git Bash (MSYS2) program is started through a short-lived intermediate process, so the Windows parent pid ofsleep.exe / grep.exe is already dead one second after start — for a perfectly healthy pipeline too. A kill that
walks the Windows parent/child tree from bash.exe therefore never reaches them. Only the MSYS process table
(ps -ef inside Git Bash) shows the real parent, the shell of the call.
Minimal reproduction
- Bash tool, foreground,
timeout: 5000:
``bash``
echo start-A; sleep 611 | grep -v zzz-repro-A | head -3; echo end-A
Result: Command did not complete within its 5s timeout and was moved to the background (ID: bwal3npo5).
- Process list at this point (pid, Windows parent, start, command):
````
8728 ppid= 44704(claude.exe) 11:06:26 bash.exe "C:\Program Files\Git\bin\bash.exe" -c "<snapshot> eval '...
45532 ppid= 8728(bash.exe) 11:06:26 bash.exe "C:\Program Files\Git\bin\..\usr\bin\bash.exe" -c "<snapshot> e...
42800 ppid= 1764(DEAD) 11:06:26 sleep.exe "C:\Program Files\Git\usr\bin\sleep.exe" 611
28240 ppid= 22980(DEAD) 11:06:26 grep.exe "C:\Program Files\Git\usr\bin\grep.exe" -v zzz-repro-A
44276 ppid= 26640(DEAD) 11:06:26 head.exe "C:\Program Files\Git\usr\bin\head.exe" -3
In the MSYS table (ps -ef) all three have the call's shell as their parent.
TaskStopwith that id →Successfully stopped task: bwal3npo5; the output file readsstart-A/[killed].
- Process list after the stop: both
bash.exeare gone;sleep.exe42800,grep.exe28240 andhead.exe44276
are still running.
Variant: the same with run_in_background: true and sleep 612 → after TaskStop only sleep.exe is left alive.
Control (works as expected): PowerShell tool, timeout: 5000, ping -n 613 127.0.0.1 | Select-String 'zzz' →
moved to the background → TaskStop → both pwsh.exe and PING.EXE are gone.
Expected
Stopping a task terminates every process the task's shell started (as it already does on Windows for native
children of PowerShell), or the tool result says that children were left running.
Actual
Successfully stopped task is reported while the children keep running without a parent.
Impact (one machine, 16 hours, three projects)
Four of five long searches traced in process snapshots were such orphans; every one began as a default-timeout
(120 s) foreground Bash call that was moved to the background and then stopped with TaskStop:
| Command | Agent action | Left running after "Successfully stopped" |
|---|---|---|
| grep -rn "…" --include=*.md --include=*.py --include=*.json -l . \| grep -v node_modules \| head -30 | TaskStop 4 s after the timeout | 13 min, 1 992 MB working set, until killed by hand |
| find / -path "*…*" -name "….json" \| head -3 | TaskStop 5 s after the timeout | ≥ 3.7 h, 3.29 million handles (it walks /proc/registry) |
| … ; du -sh . \| tail -1; … | TaskStop 11 min later | 15 min; then wrote its result into the output file after [killed] |
| … && du -sh temp \| tail -1 && … | TaskStop 8 min later | 1.5 h, long after the agent itself had finished |
The agent is told the task is stopped, so it never looks again; the memory and handles stay taken.
Suggested fix
On Windows, when the shell is Git Bash / MSYS2, kill by the MSYS process group or parent chain rather than by the
Windows parent pid: e.g. before terminating the shell, ask it for its descendants (ps -l: PID PPID PGID WINPID),
or signal them from inside (kill -TERM -- -$$ / a trap in the wrapper), or put the task's shell into a Windows
Job Object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE so that every process created under it dies with it.
Not checked
- Whether Esc / closing the session leaves the same children behind.
- Whether the task kill at a subagent's final response behaves the same way (it writes the same
[killed]mark).
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗