Bug: spawn EINVAL on Windows with Node.js v22 — all agent commands fail
Summary
On Windows with Node.js v22, acpx fails to spawn any agent (Codex, Claude, OpenCode, etc.) because child_process.spawn() returns EINVAL when trying to execute .cmd or .ps1 wrapper scripts without shell: true.
This was introduced as a security measure in Node.js v22.12+ and is documented in the Node.js changelog. All built-in agent commands (e.g., npx -y opencode-ai acp) are affected because npx on Windows is also a .ps1 wrapper.
Environment
- OS: Windows 10 (10.0.19045)
- Node.js: v22.22.0
- acpx: 0.1.15 (bundled with OpenClaw)
- Shell: PowerShell
Root Cause
In src/session-runtime/transport/process.ts (bundled as cli.js line ~1165):
import { spawn as spawn2 } from "child_process";
const child = spawn2(command, args, {
cwd: this.options.cwd,
env: buildAgentEnvironment(this.options.authCredentials),
stdio: ["pipe", "pipe", "pipe"]
// ❌ No shell: true
});
On Windows, when command resolves to a .cmd or .ps1 file (which is the case for npx, node, and any npm-installed CLI), Node.js v22 rejects the spawn with EINVAL.
Steps to Reproduce
- Install acpx on Windows with Node.js v22
- Run any agent command:
acpx opencode sessions new --name test
- Observe error:
Failed to spawn agent command: npx -y opencode-ai acp
Additional Context
- The same error occurs with custom agent overrides (e.g., setting
command to a local opencode binary that resolves to opencode.ps1)
- The
splitCommandLine function correctly splits the command string, but spawn2() doesn't use shell: true
- The Windows spawn policy in the OpenClaw plugin SDK (
applyWindowsSpawnProgramPolicy) handles this for the acpx plugin initialization, but acpx's own internal spawn calls don't use it
- No open issues exist related to Windows or
spawn EINVAL
Suggested Fix
Add shell: process.platform === "win32" to the spawn options when spawning agent commands on Windows:
const child = spawn2(command, args, {
cwd: this.options.cwd,
env: buildAgentEnvironment(this.options.authCredentials),
shell: process.platform === "win32",
stdio: ["pipe", "pipe", "pipe"]
});
Alternatively, a more targeted fix would be to only add shell: true when the resolved command ends with .cmd, .bat, or .ps1:
const needsShell = process.platform === "win32" &&
/\.(cmd|bat|ps1)$/i.test(command);
const child = spawn2(command, args, {
cwd: this.options.cwd,
env: buildAgentEnvironment(this.options.authCredentials),
shell: needsShell,
stdio: ["pipe", "pipe", "pipe"]
});
Impact
- All Windows users on Node.js v22 are affected
- Zero agent commands work (Codex, Claude, OpenCode, Gemini — all use
.cmd/.ps1 wrappers)
- The only workaround is to use the HTTP API mode (
opencode serve) instead of ACP, or downgrade Node.js to v20
Bug:
spawn EINVALon Windows with Node.js v22 — all agent commands failSummary
On Windows with Node.js v22, acpx fails to spawn any agent (Codex, Claude, OpenCode, etc.) because
child_process.spawn()returnsEINVALwhen trying to execute.cmdor.ps1wrapper scripts withoutshell: true.This was introduced as a security measure in Node.js v22.12+ and is documented in the Node.js changelog. All built-in agent commands (e.g.,
npx -y opencode-ai acp) are affected becausenpxon Windows is also a.ps1wrapper.Environment
Root Cause
In
src/session-runtime/transport/process.ts(bundled ascli.jsline ~1165):On Windows, when
commandresolves to a.cmdor.ps1file (which is the case fornpx,node, and any npm-installed CLI), Node.js v22 rejects the spawn withEINVAL.Steps to Reproduce
acpx opencode sessions new --name testFailed to spawn agent command: npx -y opencode-ai acpAdditional Context
commandto a localopencodebinary that resolves toopencode.ps1)splitCommandLinefunction correctly splits the command string, butspawn2()doesn't useshell: trueapplyWindowsSpawnProgramPolicy) handles this for the acpx plugin initialization, but acpx's own internal spawn calls don't use itspawn EINVALSuggested Fix
Add
shell: process.platform === "win32"to the spawn options when spawning agent commands on Windows:Alternatively, a more targeted fix would be to only add
shell: truewhen the resolved command ends with.cmd,.bat, or.ps1:Impact
.cmd/.ps1wrappers)opencode serve) instead of ACP, or downgrade Node.js to v20