[BUG] `claude agents --json` leaves suspended `git ... remote` children behind (Windows)
What's Wrong?
On Windows, claude agents --json often leaves a git.exe process behind. The process is created suspended and is never resumed. Its parent claude process exits, so the git process is orphaned for good. It has one thread with wait reason Suspended, uses no CPU, and holds about 2 MB of working set.
It does not happen on every call. On 2.1.296, 10 consecutive calls left 5 orphans. In another stretch of the same evening, 95 calls left none. I have not found what decides it.
They accumulate. On one machine, scripts that poll claude agents --json left 268 of them over about 5.5 hours.
The orphan was created with handle inheritance on, so it also keeps a copy of every inheritable handle of its parent chain. A caller that reads claude agents --json through a pipe never sees EOF: the orphan keeps the write end of the caller's stdout pipe open. For example, cmd /c "claude agents --json <NUL >NUL 2>NUL" run from a PowerShell host whose output is piped does not return until the orphan is killed.
claude rm <id> left one as well, so the code path may be shared by other subcommands.
Every orphan has this command line:
"<scoop>\apps\git\current\cmd\git.exe" -c core.askPass= -c protocol.ext.allow=never -c submodule.recurse=false -c log.showSignature=false -c format.pretty=medium -c gc.auto=0 -c maintenance.auto=false -c hook.applypatch-msg.enabled=false ... -c hook.post-index-change.enabled=false -c core.hooksPath=\\.\NUL -c core.fsmonitor= remote
(The ... stands for one -c hook.<name>.enabled=false per git hook.)
What Should Happen?
claude agents --json should leave no child processes behind. If it starts git remote, it should resume the child and wait for it, or terminate it before it exits.
Steps to Reproduce
In PowerShell 7 on Windows:
foreach ($i in 1..10) {
$before = @(Get-Process git -ErrorAction SilentlyContinue).Id
claude agents --json > $null
Start-Sleep 2
Get-Process git | Where-Object { $_.Id -notin $before } |
ForEach-Object { 'call {0}: {1} {2}' -f $i, $_.Id, $_.Threads[0].WaitReason }
}
Calls that leak print a new pid with wait reason Suspended. Get-CimInstance Win32_Process -Filter "ProcessId=<pid>" shows a ParentProcessId that no longer exists. Stop-Process -Id <pid> removes it, and nothing else is affected.
Is this a regression?
I don't know. It was first noticed on 2.1.289 and is still present on 2.1.296. Earlier versions were not checked for it.
Claude Code Version
2.1.296
Platform
Anthropic API
Operating System
Windows 11 Pro (build 26300)
Terminal/Shell
PowerShell 7
Additional Information
Possibly related, but a different mechanism: #97752 (a timed-out git status whose real git.exe outlives its killed launcher). Here the launcher itself is left suspended and never runs.
Workaround: we now stop any git.exe that meets all of these conditions: every thread is suspended, its parent is gone, it is more than 60 seconds old, and its command line ends in -c core.fsmonitor= remote. We also clear HANDLE_FLAG_INHERIT on our std handles around the call, so the orphan cannot hold our pipes.