Skip to content

Fix Windows "Could not create child process" for npm-installed CLIs#420

Merged
dsarno merged 2 commits into
mainfrom
claude/fix-mcp-process-error-Idqjc
May 11, 2026
Merged

Fix Windows "Could not create child process" for npm-installed CLIs#420
dsarno merged 2 commits into
mainfrom
claude/fix-mcp-process-error-Idqjc

Conversation

@dsarno

@dsarno dsarno commented May 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the Discord report:

ERROR: Could not create child process: "C:\Program Files\nodejs\claude" mcp list

Root cause

npm-on-Windows installs claude as two files in C:\Program Files\nodejs\:

  • claude — a POSIX bash shim for Git Bash / WSL users (no extension)
  • claude.cmd — the actual Windows batch wrapper

where claude lists both, extensionless first. McpCliFinder._resolve was taking output[0].split("\n")[0] and handing the bash shim to OS.execute_with_pipe, which goes through CreateProcessW. CreateProcessW refuses to launch the extensionless shim and surfaces the "Could not create child process" line in Godot's output log. Every dock client-status probe (claude mcp list, configure, remove) tripped this.

A side effect users notice: the troubleshooting prompt under "server exited before WebSocket handshake" tells them to check Godot's output for Python's traceback, and the very loud claude error sits right there, so it gets attributed to the server-spawn problem even though it doesn't actually crash the server.

Fix (two layers, on purpose)

  1. _cli_finder.gd — on Windows, prefer paths ending in .exe / .cmd / .bat / .com over extensionless entries in where output. Falls back to the first non-empty line so a host returning only an extensionless match still gets something to try.
  2. _cli_exec.gd — on Windows, wrap .cmd / .bat invocations as cmd.exe /c <exe> <args>. CreateProcessW won't launch shell scripts on its own; this defends against future resolvers handing back a .cmd path directly.

Both layers are needed: layer 1 stops the bug at resolution time; layer 2 guarantees a .cmd that arrives through any other path still spawns correctly.

Test plan

  • pytest -v — 904 passed (no Python-side changes; sanity check only)
  • ruff check src/ tests/ — clean
  • Open test_project/ in Godot on a Windows host with npm-installed claude
    • test_run suite=cli_finder_pick_best_path invariants pass (npm reproducer, .exe > .cmd, CRLF endings, fallback, blanks, empty input)
    • test_run suite=cli_exec — the new Windows .cmd smoke captures stdout through the cmd.exe wrap; existing Unix-side tests still pass
    • Dock client status row for Claude Code shows CONFIGURED / NOT_CONFIGURED instead of the spawn-failed state; Godot output log is free of the "Could not create child process" line
  • Open test_project/ in Godot on macOS or Linux
    • test_run suite=cli_finder — extension preference rules are platform-pure and still pass (no host PATH dependency)
    • test_run suite=cli_exec — the new Windows-only smoke is skipped; rest is unchanged

https://claude.ai/code/session_01C5WkB2x3RmUCmhzdnYzEes


Generated by Claude Code

When `claude` is installed via npm on Windows, the wrapper directory
holds both `claude` (a POSIX bash shim for Git Bash / WSL) and
`claude.cmd` (the actual Windows batch wrapper). `where claude` lists
both, with the extensionless shim first. `McpCliFinder._resolve` was
picking the first line, and `OS.execute_with_pipe` then tripped on
`CreateProcessW` — surfacing `ERROR: Could not create child process:
"C:\Program Files\nodejs\claude" mcp list` every time the dock probed
client status.

Two-layer fix so a single fragile assumption can't reintroduce the bug:

1. `_cli_finder.gd`: on Windows, prefer paths with `.exe` / `.cmd` /
   `.bat` / `.com` extensions over extensionless entries in `where`
   output. Falls back to the first non-empty line so we never come up
   empty when `where` returned something.
2. `_cli_exec.gd`: on Windows, wrap `.cmd` / `.bat` invocations as
   `cmd.exe /c <exe> <args>`. CreateProcessW can't launch shell scripts
   directly, so even if a future resolver returns a `.cmd` path, the
   spawn now succeeds.

Tests:
- `test_cli_finder.gd` pins `_pick_best_path` against the npm-style
  reproducer plus CRLF line endings, `.exe`-beats-`.cmd` preference,
  blank-line handling, and the fallback path.
- `test_cli_exec.gd` adds a Windows-only smoke that writes a temp
  `.cmd` and confirms `McpCliExec.run` captures its stdout through the
  new wrap.
@codecov

codecov Bot commented May 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

The previous implementation iterated lines outer / extensions inner — it
returned the first line that matched *any* recognised extension, which
let a `.cmd` listed before `.exe` win. The added test
`test_pick_best_path_prefers_exe_over_cmd` caught this and turned the
Linux / macOS / Windows Godot-test jobs red.

Swap the loop order so `_WINDOWS_EXEC_EXTS` order is the actual
preference: `.exe` is consulted against every line before `.cmd` is
considered. Strip and filter blanks up front so both the matching pass
and the fallback work off the same list.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Windows spawning failures for npm-installed CLIs (e.g., claude) by ensuring the plugin resolves a Windows-launchable executable path from where output and correctly executes .cmd/.bat wrappers via cmd.exe, preventing Godot’s CreateProcessW errors during dock client probes/configure/remove flows.

Changes:

  • Update CLI path resolution on Windows to prefer .exe/.cmd/.bat/.com entries over extensionless shims in where output.
  • Wrap .cmd/.bat execution on Windows as cmd.exe /c <script> <args> to ensure OS.execute_with_pipe can spawn them.
  • Add focused Godot test suites covering Windows path-picking invariants and Windows .cmd execution wrapping behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test_project/tests/test_cli_finder.gd.uid Adds Godot UID for the new CLI finder test suite.
test_project/tests/test_cli_finder.gd New tests for Windows where output path selection via _pick_best_path.
test_project/tests/test_cli_exec.gd Adds a Windows-only regression test to ensure .cmd wrappers spawn via cmd.exe /c.
plugin/addons/godot_ai/clients/_cli_finder.gd Implements Windows-aware best-path selection from where output.
plugin/addons/godot_ai/clients/_cli_exec.gd Ensures .cmd/.bat are executed through cmd.exe on Windows.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@dsarno dsarno merged commit d880ac9 into main May 11, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants