-
Notifications
You must be signed in to change notification settings - Fork 1.3k
app-server.mjs:188 — spawn("codex") fails on Windows without shell:true #337
Description
Summary
On Windows, the codex-companion plugin fails to start the codex app-server because
child_process.spawn("codex", ...) at scripts/lib/app-server.mjs:188 is invoked
without { shell: true }. Node on Windows does not auto-resolve PATHEXT (.cmd,
.ps1) extensions for spawn unless shell: true is set, so the npm-installed
codex.cmd wrapper is invisible. Result: spawn codex ENOENT.
This causes two user-visible failure modes downstream:
codex-audit/ directreviewinvocations fail fast withspawn codex ENOENT.codex:rescue(taskmode) blocks the foreground for an extended period
(observed ~50 minutes) and/codex:statusblocks ~2 hours before erroring,
apparently retrying the failed spawn without surfacing the underlying ENOENT.
Environment
- OS: Windows 11 Home (10.0.26200)
- Node: v22.14.0
- npm: 10.9.2
- codex-cli: 0.131.0 (installed globally via
npm i -g) - Claude Code with
openai-codexplugin (latest as of 2026年05月19日) - Shell: PowerShell 5.1 (main session); subagent dispatches use Bash via Git for Windows
Reproduction
From PowerShell, codex --version succeeds:
PS> (Get-Command codex).Source
C:\Users\<user>\AppData\Roaming\npm\codex.ps1
PS> codex --version
codex-cli 0.131.0
But Node spawn without shell fails:
PS> node -e 'const{spawn}=require("child_process");const p=spawn("codex",["--version"]);p.on("error",e=>console.log(e.code));'
ENOENT
With shell: true, it succeeds:
PS> node -e 'const{spawn}=require("child_process");const p=spawn("codex",["--version"],{shell:true});p.stdout.on("data",d=>process.stdout.write(d));'
codex-cli 0.131.0
Root cause
scripts/lib/app-server.mjs:188:
this.proc = spawn("codex", ["app-server"], { cwd: this.cwd, env: this.options.env, stdio: ["pipe", "pipe", "pipe"] });
The plugin already uses the correct pattern elsewhere — scripts/lib/process.mjs:11
sets shell: process.platform === "win32" for its runCommand spawns. The
app-server spawn was missed.
Proposed fix
One-line addition:
this.proc = spawn("codex", ["app-server"], {
cwd: this.cwd,
env: this.options.env,
- stdio: ["pipe", "pipe", "pipe"]
+ stdio: ["pipe", "pipe", "pipe"],
+ shell: process.platform === "win32"
});Verified locally: applying this patch unblocks the codex-rescue dispatch path
(smoke test completed end-to-end in 53s vs. indefinitely hanging before).
Why this matters
The hang failure mode is particularly costly because it masquerades as Codex
"still working" rather than a config error — users wait for hours before
realizing the task is wedged. A clean ENOENT surface would be better than the
current silent-hang behavior.