taskkill /PID flag mangled by Git Bash MSYS path translation on Windows

Open 💬 0 comments Opened Jun 8, 2026 by kefyusuf

Summary

terminateProcessTree() fails silently on Windows when the Node.js process is launched from Git Bash (e.g. via the Claude Code CLI or any MSYS2-based terminal). The cancel companion command returns exit 1 and running processes are never stopped.

Root cause

In scripts/lib/process.mjs, runCommand() sets:

shell: process.platform === "win32" ? (process.env.SHELL || true) : false,

In a Git Bash session, process.env.SHELL is /usr/bin/bash (or similar). spawnSync then spawns that shell as the interpreter. Git Bash applies MSYS automatic path translation to all arguments that look like Unix paths: /PIDC:/Program Files/Git/PID, /TC:/Program Files/Git/T, /FC:/Program Files/Git/F.

So the actual command that reaches taskkill.exe is:

taskkill "C:/Program Files/Git/PID" 65784 "C:/Program Files/Git/T" "C:/Program Files/Git/F"

which exits 1 with ERROR: Invalid argument/option - 'C:/Program Files/Git/PID'.

Reproduction

  1. Open Git Bash on Windows.
  2. Launch Claude Code (or any tool that uses codex-companion.mjs).
  3. Start a long-running codex task and try to cancel it.
  4. cancel <job-id> returns an error; the process keeps running.

Confirmed on: Windows 11 Pro 10.0.26200, Git for Windows 2.x, Node.js 20+.

Error output (observed)

taskkill /PID 65784 /T /F: exit=1: ERROR: Invalid argument/option - 'C:/Program Files/Git/PID'

Workaround

Set MSYS_NO_PATHCONV=1 before invoking the companion:

MSYS_NO_PATHCONV=1 node scripts/codex-companion.mjs cancel <job-id>

Suggested fix

For the taskkill invocation specifically, either:

Option A — force cmd.exe instead of process.env.SHELL for Win32 so MSYS translation never runs:

// In runCommand():
shell: process.platform === "win32" ? "cmd.exe" : false,

Option B — inject MSYS_NO_PATHCONV=1 into the env when calling terminateProcessTree():

const result = runCommandImpl("taskkill", ["/PID", String(pid), "/T", "/F"], {
  cwd: options.cwd,
  env: { MSYS_NO_PATHCONV: "1", ...(options.env ?? process.env) },
});

Option C — use shell: false for taskkill directly (it is a native Windows executable and does not need a shell wrapper). This would require passing a custom runCommandImpl or adding a noShell option to runCommand.

Option A is the lowest-risk single-line change; cmd.exe is always available on Win32 and does not apply path mangling.

View original on GitHub ↗