summary
on windows, every turn from a claude code agent fails with -32603 internal error, and the hidden detail is spawn EINVAL. the desktop sets CLAUDE_CODE_EXECUTABLE to the claude.cmd npm shim, and the adapter spawns it without shell: true. node >= 20.12 / 24 refuses to spawn a .cmd/.bat without a shell (the cve-2024-27980 hardening), so the spawn throws EINVAL. this is a sibling of the .cmd handling in #2247, and separate from the runtime path issue in #2327.
environment
- os: windows 11
- runtime: claude code,
claude cli v2.1.217 (npm global, so the shim is %APPDATA%\npm\claude.cmd)
- adapter:
@agentclientprotocol/claude-agent-acp v0.60.0
- node v24
what happens
buzz shows the agent's harness reported an internal error, and buzz-acp logs:
WARN buzz_acp: agent_returned (application error) outcome="error" configured_model=haiku error=Agent reported error (code -32603): Internal error
root cause
configure_runtime_cli (desktop/src-tauri/src/managed_agents/runtime.rs) does:
if let Some(cli_path) = runtime.underlying_cli.and_then(resolve_command) {
command.env("CLAUDE_CODE_EXECUTABLE", cli_path);
}
for the claude runtime, resolve_command("claude") returns the npm shim ...\npm\claude.cmd. the adapter then spawns CLAUDE_CODE_EXECUTABLE, and on node >= 20.12/24 spawning a .cmd without shell: true throws spawn EINVAL.
note also that configure_runtime_cli runs after the user-env writes (runtime.rs ~line 1862, after merged_user_env at ~1859), so users can't override CLAUDE_CODE_EXECUTABLE from edit agent env vars.
reproduction
driving the adapter directly reproduces it:
CLAUDE_CODE_EXECUTABLE=<...>\claude.cmd -> session/new -> {"code":-32603,"message":"Internal error","data":{"details":"spawn EINVAL"}}
CLAUDE_CODE_EXECUTABLE="" -> turn succeeds (adapter falls back to its bundled claude-code)
suggested fixes
- on windows, resolve
underlying_cli to a spawnable target (prefer a native claude.exe over the .cmd shim), or
- spawn the executable with
shell: true on windows, or
- don't set
CLAUDE_CODE_EXECUTABLE to a bare .cmd; leaving it unset makes the adapter use its bundled claude-code, which works fine.
workaround
install claude code's native binary so a real claude.exe is on path (irm https://claude.ai/install.ps1 | iex, lands at %USERPROFILE%\.local\bin\claude.exe). resolve_command prefers .exe over .cmd, so the adapter then spawns a real executable and turns succeed.
happy to open a pr if that would help!
summary
on windows, every turn from a claude code agent fails with
-32603 internal error, and the hidden detail isspawn EINVAL. the desktop setsCLAUDE_CODE_EXECUTABLEto theclaude.cmdnpm shim, and the adapter spawns it withoutshell: true. node >= 20.12 / 24 refuses to spawn a.cmd/.batwithout a shell (the cve-2024-27980 hardening), so the spawn throwsEINVAL. this is a sibling of the.cmdhandling in #2247, and separate from the runtime path issue in #2327.environment
claudecli v2.1.217 (npm global, so the shim is%APPDATA%\npm\claude.cmd)@agentclientprotocol/claude-agent-acpv0.60.0what happens
buzz shows
the agent's harness reported an internal error, and buzz-acp logs:root cause
configure_runtime_cli(desktop/src-tauri/src/managed_agents/runtime.rs) does:for the
clauderuntime,resolve_command("claude")returns the npm shim...\npm\claude.cmd. the adapter then spawnsCLAUDE_CODE_EXECUTABLE, and on node >= 20.12/24 spawning a.cmdwithoutshell: truethrowsspawn EINVAL.note also that
configure_runtime_cliruns after the user-env writes (runtime.rs ~line 1862, aftermerged_user_envat ~1859), so users can't overrideCLAUDE_CODE_EXECUTABLEfrom edit agent env vars.reproduction
driving the adapter directly reproduces it:
suggested fixes
underlying_clito a spawnable target (prefer a nativeclaude.exeover the.cmdshim), orshell: trueon windows, orCLAUDE_CODE_EXECUTABLEto a bare.cmd; leaving it unset makes the adapter use its bundled claude-code, which works fine.workaround
install claude code's native binary so a real
claude.exeis on path (irm https://claude.ai/install.ps1 | iex, lands at%USERPROFILE%\.local\bin\claude.exe).resolve_commandprefers.exeover.cmd, so the adapter then spawns a real executable and turns succeed.happy to open a pr if that would help!