Agent fails to spawn claude on Windows when the CLI is installed via npm (.ps1/.cmd shim) #63

Closed
opened 2026-07-15 09:53:32 -07:00 by mthelm85 · 2 comments
mthelm85 commented 2026-07-15 09:53:32 -07:00 (Migrated from github.com)

On Windows, an npm-installed Claude Code CLI provides claude.ps1 / claude.cmd shims on PATH rather than an .exe. Kaimon's agent service spawns `claude -p ...` directly, and libuv/Base.run only resolves real executables — so opening the agent fails with:

Error opening agent: IOError: could not spawn setenv(`claude -p --input-format stream-json ...`): no such file or directory (ENOENT)

...even though claude --version works fine in the user's shell.

Installing the native build (irm https://claude.ai/install.ps1 | iex, which drops claude.exe into ~/.local/bin) fixes it.

Suggestions (any subset):

  • Detect the situation: if spawning fails and Sys.which("claude") === nothing while a claude.cmd/claude.ps1 exists on PATH, raise a clear error telling the user to install the native CLI.
  • Or spawn via cmd /c claude ... on Windows when only a shim is present.
  • Docs: note that on Windows the native installer is required for the agent.

Also worth noting: the ENOENT error message currently dumps the entire environment block (including any API keys in the user's environment) into the UI — trimming that would avoid accidental secret leaks when users paste errors into issues.

On Windows, an npm-installed Claude Code CLI provides `claude.ps1` / `claude.cmd` shims on PATH rather than an `.exe`. Kaimon's agent service spawns `` `claude -p ...` `` directly, and libuv/`Base.run` only resolves real executables — so opening the agent fails with: ``` Error opening agent: IOError: could not spawn setenv(`claude -p --input-format stream-json ...`): no such file or directory (ENOENT) ``` ...even though `claude --version` works fine in the user's shell. Installing the native build (`irm https://claude.ai/install.ps1 | iex`, which drops `claude.exe` into `~/.local/bin`) fixes it. Suggestions (any subset): - Detect the situation: if spawning fails and `Sys.which("claude") === nothing` while a `claude.cmd`/`claude.ps1` exists on PATH, raise a clear error telling the user to install the native CLI. - Or spawn via `cmd /c claude ...` on Windows when only a shim is present. - Docs: note that on Windows the native installer is required for the agent. Also worth noting: the ENOENT error message currently dumps the entire environment block (including any API keys in the user's environment) into the UI — trimming that would avoid accidental secret leaks when users paste errors into issues.
kahliburke commented 2026-07-17 00:07:35 -07:00 (Migrated from github.com)

Ah Windows ... ;) will try to take a look in a few days.

Ah Windows ... ;) will try to take a look in a few days.
kahliburke commented 2026-07-17 19:16:53 -07:00 (Migrated from github.com)

Fixed by #66 (merged to main; ships in the next release — it landed just after the 2.0.8 bump, so it'll be in 2.0.9, not 2.0.8).

Root cause (thanks for the sharp report — and for pinpointing the shim angle): on Windows, Sys.which resolves a bare command name only as .exe — it ignores PATHEXT entirely. So an npm-installed claude (a .cmd + .ps1 shim, no .exe) is invisible to Sys.which, and thus to Base.run/libuv, which ENOENTs — even though claude --version works in your shell. Confirmed on a fresh Windows instance: Sys.which("claude") returns nothing while Sys.which("claude.cmd") resolves fine.

Two fixes:

  1. Shim resolutionlaunch_argv now falls back to a PATH × PATHEXT search when Sys.which comes up empty, finds the .cmd/.ps1, and wraps it through its interpreter (cmd.exe /d /c / powershell -File) as before. So the npm CLI works directly — the native installer is no longer required. Covers every CLI spawn (claude/gemini/codex MCP commands and the agent). Verified end-to-end on real Windows: bare claude → resolved to the .cmd → wrapped → spawns with args passed through.

  2. The secret leak (the important note at the bottom of your report) — a failed spawn's IOError stringified the whole setenv(cmd, env), including the process environment, surfaced verbatim in the UI. Fixed: spawn failures now re-raise a sanitized error built from the argv alone (no env), with an actionable hint. No more dumping ANTHROPIC_API_KEY into a pasteable error.

Fixed by #66 (merged to `main`; ships in the next release — it landed just after the 2.0.8 bump, so it'll be in 2.0.9, not 2.0.8). **Root cause** (thanks for the sharp report — and for pinpointing the shim angle): on Windows, `Sys.which` resolves a bare command name *only* as `.exe` — it ignores `PATHEXT` entirely. So an npm-installed `claude` (a `.cmd` + `.ps1` shim, no `.exe`) is invisible to `Sys.which`, and thus to `Base.run`/libuv, which ENOENTs — even though `claude --version` works in your shell. Confirmed on a fresh Windows instance: `Sys.which("claude")` returns `nothing` while `Sys.which("claude.cmd")` resolves fine. **Two fixes:** 1. **Shim resolution** — `launch_argv` now falls back to a `PATH` × `PATHEXT` search when `Sys.which` comes up empty, finds the `.cmd`/`.ps1`, and wraps it through its interpreter (`cmd.exe /d /c` / `powershell -File`) as before. So the npm CLI works directly — the native installer is no longer required. Covers every CLI spawn (`claude`/`gemini`/`codex` MCP commands and the agent). Verified end-to-end on real Windows: bare `claude` → resolved to the `.cmd` → wrapped → spawns with args passed through. 2. **The secret leak** (the important note at the bottom of your report) — a failed spawn's `IOError` stringified the whole `setenv(cmd, env)`, including the process environment, surfaced verbatim in the UI. Fixed: spawn failures now re-raise a sanitized error built from the argv alone (no env), with an actionable hint. No more dumping `ANTHROPIC_API_KEY` into a pasteable error.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kahliburke/Kaimon.jl#63
No description provided.