Description
Description
The Bash tool (packages/core/src/tool/bash.ts) creates child processes with detached: process.platform !== "win32" (L163). On Windows, detached is false, so no separate process group is created.
The kill path (packages/core/src/process/cross-spawn-spawner.ts) already has a killGroup function (L292-L312) that uses taskkill /pid ${proc.pid} /T /F on Windows to attempt tree-wide cleanup. However, a TOCTOU (time-of-check-time-of-use) race condition exists: when the shell command (e.g., cmd /c start /b ping -t localhost) spawns a child process, that child may become its own process group leader before taskkill /T enumerates the tree. The /T flag only kills processes that are direct descendants at the moment of execution. If a grandchild process is spawned during the window between shell start and timeout execution, it can escape cleanup.
Additionally, cmd /c combined with start /b creates sibling processes (not children of cmd.exe), which taskkill /T /F may miss because they exist outside the PID-rooted tree.
Steps to reproduce
- On Windows, ask the model to run:
start /b ping -t localhost - Wait for the Bash tool timeout (~30s default)
- The shell process is killed via
taskkill /pid <cmd_pid> /T /F, butping.exemay survive because it was created as a sibling rather than a child of cmd.exe - Repeat — multiple orphan
ping.exeprocesses accumulate in Task Manager
Expected behavior
Replace taskkill /T /F with Windows Job Objects (CreateJobObject/AssignProcessToJobObject) to guarantee tree-wide termination regardless of process group topology. Job objects cannot be escaped by child processes and survive across all race conditions.
Code location
- https://github.com/anomalyco/opencode/blob/dev/packages/core/src/tool/bash.ts#L163 (detached flag)
- https://github.com/anomalyco/opencode/blob/dev/packages/core/src/process/cross-spawn-spawner.ts#L292-L312 (killGroup function)
Environment
OpenCode Desktop 1.17.8, Windows 11
About the duplicate flag: The referenced issues cover related Windows process-lifecycle bugs but with different focuses:
- allowed-tools: Bash(agent-browser:*) 衍生的 Chrome 子进程在会话中断后变成孤儿进程 #29856 (orphan child process leak when sessions are interrupted) — similar symptom but the root cause is session interruption, not TOCTOU during timeout-based cleanup
- bash tool hangs when command spawns background child processes #20902 (bash tool hangs when commands spawn background child processes) — about stdout/stderr pipe blocking, not orphan processes
- Bash tool hangs until timeout on Windows when spawned process keeps a stdout/stderr pipe open (dev servers, daemons) #32504 (Windows-specific hang until timeout) — about pipe blocking, same as bash tool hangs when command spawns background child processes #20902
This issue specifically identifies a TOCTOU race in the taskkill /T /F approach and proposes Windows Job Objects as the fix, which is a distinct root cause and solution from the other issues.
Activity
github-actions commentedon Jun 20, 2026
This issue might be a duplicate of existing issues. Please check:
These are related Windows/process-lifecycle issues and may overlap with or inform this report.
Necmttn commentedon Jun 21, 2026
Windows process cleanup needs job-object or process-tree ownership, not only best-effort
taskkill /Tafter timeout. Create the shell and descendants under an owned job when possible, track spawned child pids, and close/kill the job on timeout, abort, or session exit.At minimum, write a process cleanup receipt with root pid, known descendants, missed/escaped pids, timeout reason, and final kill result. That makes orphan leaks visible instead of leaving users to hunt high-CPU ping/cmd processes manually.
Generated with ax.
rekram1-node commentedon Jun 22, 2026
STOP SPAMMING ISSUES.