Skip to content

fix(subprocess): harden process spawning on Windows (MSYS paths + process-tree kill) - #93

Open
Zlatanwic wants to merge 3 commits into
SJTU-IPADS:mainfrom
Zlatanwic:fix/subprocess-windows-robustness
Open

fix(subprocess): harden process spawning on Windows (MSYS paths + process-tree kill)#93
Zlatanwic wants to merge 3 commits into
SJTU-IPADS:mainfrom
Zlatanwic:fix/subprocess-windows-robustness

Conversation

@Zlatanwic

Copy link
Copy Markdown
Contributor

Summary

Two robustness fixes to src/core/subprocess.ts for spawning external processes, both surfaced while running the benchmark harness on Windows (Git Bash / MSYS).

1. Resolve MSYS drive paths for Bun.spawn on Windows

Under Git Bash, absolute paths arrive MSYS-style (/d/SkVM/...). Bun.spawn on Windows expects native paths (D:/SkVM/...), so cmd[0] could fail to resolve. This normalizes the spawn executable path before handing it to Bun.

2. Kill the whole process tree on timeout + cancel pipe readers

On timeout we previously killed only the direct child, leaving grandchildren (e.g. a shell that forked a long-running tool) orphaned. This now:

  • kills the entire process tree, and
  • cancels the stdout/stderr pipe readers so the call returns promptly with whatever partial output was captured instead of hanging on a still-open pipe.

Why it's standalone

These are pure subprocess.ts internals with no signature changes to runSubprocess. No other module needs to change.

Test plan

  • bunx tsc --noEmit passes
  • Verified on Windows Git Bash: MSYS /d/... paths spawn correctly
  • Verified process-tree teardown on timeout (no orphaned grandchildren)

Scope

Additive/hardening only. No breaking changes.

Zlatanwic added 2 commits July 7, 2026 22:38
Adapters' tierGlobal resolvers feed `which <name>` output straight back
as cmd[0]. Under Git Bash / MSYS that returns MSYS drive paths (`/d/...`)
which Bun.spawn cannot resolve, producing ENOENT uv_spawn. Convert
`/x/...` to `X:/...` (trying .exe for bun-compiled binaries like
node_modules/.bin/pi) on win32; no-op elsewhere and for bare PATH names.
runSubprocess's timeout only called proc.kill() (SIGTERM to the direct
child). Adapter wrappers (pi.exe, opencode) spawn grandchildren that survive
and keep the stdout pipe open, so proc.exited / Response.text() never resolve
and the timeout never actually fires — a stuck agent hangs the whole bench
run (observed: pi on tb-adaptive-rejection-sampler ran 11min past a 600s
timeout, bun at 4.65GB, zero tasks completed).

Two fixes:
- killProcessTree: taskkill /T /F on Windows (SIGKILL the group elsewhere) to
  take down the wrapper AND its descendants.
- Replace Response(stream).text() with cancellable reader.read() loops; on
  timeout we cancel the readers so runSubprocess returns promptly with
  partial output instead of blocking on a pipe held open by a killed
  process's orphaned grandchild.

Verified: sleep 30 with 2s timeout now returns in ~2.6s (was 30s).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the runSubprocess implementation to be more robust on Windows (Git Bash / MSYS) by normalizing MSYS-style absolute paths before spawning, and by improving timeout teardown to avoid hangs caused by still-open stdout/stderr pipes.

Changes:

  • Normalize MSYS /d/... drive paths (and optionally add .exe) for cmd[0] before calling Bun.spawn on Windows.
  • On timeout, attempt to kill the whole process tree (Windows taskkill /T /F, POSIX process-group SIGKILL) and cancel stdout/stderr readers to return promptly with partial output.
  • Replace Response(stream).text() with manual stream readers for stdout/stderr to support cancellation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/core/subprocess.ts
Comment thread src/core/subprocess.ts
Comment thread src/core/subprocess.ts
Comment thread src/core/subprocess.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants