Skip to content

cancel mangles taskkill flags on Windows when SHELL points to Git Bash #182

@lumatic2

Description

@lumatic2

Summary

On Windows, codex-companion cancel <jobId> fails when process.env.SHELL points to Git Bash (or any MSYS-rooted shell). The taskkill flags get path-converted by MSYS into nonsense like C:/Program Files/Git/PID 12345 C:/Program Files/Git/T C:/Program Files/Git/F, taskkill rejects them, and the cancel surfaces as Error: taskkill ... exit=1: ERROR: ....

Plugin version: 1.0.3
File: `scripts/lib/process.mjs`

Repro

  1. Windows + Git for Windows installed.
  2. `export SHELL="/usr/bin/bash"` (Git Bash default) or run inside a Git Bash session that exports `SHELL`.
  3. Start a long codex job: `node codex-companion.mjs task --background -- "sleep 60"`.
  4. `node codex-companion.mjs cancel --json`.

Expected: job cancelled. Actual: `terminateProcessTree` throws because spawned `taskkill` ran with mangled args.

Root cause

`scripts/lib/process.mjs:12`:

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

When `SHELL` is set to a bash from a Git for Windows install, `spawnSync` runs the command through bash. MSYS path conversion then rewrites every argv element starting with `/` (like `/PID`, `/T`, `/F`) into a `C:/Program Files/Git/...` path, because MSYS treats them as Unix-style paths. `taskkill` sees no recognizable options and exits non-zero.

Suggested fix

`taskkill` is a Win32 .exe — there's no need to go through a shell at all. Either:

  1. Direct invocation (preferred): in `terminateProcessTree`, call `runCommand` with an option that forces `shell: false` (or spawn `taskkill.exe` directly). The function already has a clean argv.
  2. Drop `SHELL` reliance in `runCommand` for Windows: `shell: true` (cmd.exe) instead of `shell: SHELL || true`. `cmd.exe` doesn't path-convert.

Option 1 is the smaller and safer change.

Related (separate but adjacent)

`looksLikeMissingProcessMessage` only matches English (`not found|no running instance|cannot find|...`). On Korean Windows the localized "프로세스를 찾을 수 없습니다" doesn't match, so an already-dead PID still throws via `formatCommandFailure`. Worth either matching by `errorlevel` (taskkill returns 128 for "not found") or extending the regex with non-English variants — the former is locale-proof.

Workaround

In my MCP wrapper (`codex-mcp/src/codex-exec.mjs`) I strip `SHELL` from the child env before spawning the companion on Windows. This forces the cmd.exe fallback inside `runCommand`. Confirmed `taskkill /PID /T /F` then runs cleanly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions