Skip to content

Windows: spawn("codex") in app-server.mjs throws ENOENT (Node does not try PATHEXT for .cmd shims) #287

@shihchengwei-lab

Description

@shihchengwei-lab

Environment

  • OS: Windows 11
  • Node: v22.x
  • codex: 0.128.0 (npm global install)
  • Plugin: openai-codex 1.0.1

Symptom

On Windows, invoking codex:rescue (or any path that triggers CodexAppServerClient.connect) fails with:

Error: spawn codex ENOENT

/codex:setup reports ready: true because detection takes a different code path that already uses shell: true, masking the issue.

Root cause

scripts/lib/app-server.mjs:188 calls:

this.proc = spawn("codex", ["app-server"], {
  cwd: this.cwd,
  env: this.options.env,
  stdio: ["pipe", "pipe", "pipe"]
});

On Windows, child_process.spawn does not consult PATHEXT, so it cannot resolve a .cmd shim. The npm global install of @openai/codex only ships:

codex       (sh shim, no extension)
codex.cmd
codex.ps1

There is no codex.exe, so spawn falls through with ENOENT.

By contrast, scripts/lib/process.mjs:5 already uses shell: process.platform === "win32" for the same reason — that is why binaryAvailable("codex", ...) works during /codex:setup.

Reproduction

On Windows with codex installed via npm i -g @openai/codex:

// fails with ENOENT
require('child_process').spawn('codex', ['--version']).on('error', console.error);

// works
require('child_process').spawn('codex', ['--version'], { shell: true }).on('error', console.error);

Diagnostic note

Stderr from a failed task-worker often shows a DEP0190 deprecation warning and the ENOENT side by side. They come from different spawn points:

  • DEP0190 originates from process.mjs (uses shell: true + args array — Node 22+ warns on this combination, but the spawn itself succeeds).
  • ENOENT originates from app-server.mjs:188 (no shell: true, fails to resolve the .cmd shim).

Easy to misread as "shell:true is already set but spawn still fails."

Suggested fix

Two options:

  1. Match the existing pattern in process.mjs — add shell: process.platform === "win32" to the spawn options at app-server.mjs:188. One-line change. Caveat: on Windows this wraps the child in cmd.exe, so this.proc.kill() may leave the actual codex process as an orphan.

  2. Resolve the executable's full path before spawning — try codex, codex.cmd, codex.exe against PATHEXT (or use a small helper / cross-spawn) and spawn the resolved path directly. No shell wrapper, kill targets the codex process directly. Cleaner long-term fix.

Option 2 is preferable; option 1 is a one-line patch that unblocks Windows users immediately.

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