Fix Windows "Could not create child process" for npm-installed CLIs#420
Merged
Conversation
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 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.
Contributor
There was a problem hiding this comment.
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/.comentries over extensionless shims inwhereoutput. - Wrap
.cmd/.batexecution on Windows ascmd.exe /c <script> <args>to ensureOS.execute_with_pipecan spawn them. - Add focused Godot test suites covering Windows path-picking invariants and Windows
.cmdexecution 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the Discord report:
Root cause
npm-on-Windows installs
claudeas two files inC:\Program Files\nodejs\:claude— a POSIX bash shim for Git Bash / WSL users (no extension)claude.cmd— the actual Windows batch wrapperwhere claudelists both, extensionless first.McpCliFinder._resolvewas takingoutput[0].split("\n")[0]and handing the bash shim toOS.execute_with_pipe, which goes throughCreateProcessW. 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
claudeerror 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)
_cli_finder.gd— on Windows, prefer paths ending in.exe/.cmd/.bat/.comover extensionless entries inwhereoutput. Falls back to the first non-empty line so a host returning only an extensionless match still gets something to try._cli_exec.gd— on Windows, wrap.cmd/.batinvocations ascmd.exe /c <exe> <args>. CreateProcessW won't launch shell scripts on its own; this defends against future resolvers handing back a.cmdpath directly.Both layers are needed: layer 1 stops the bug at resolution time; layer 2 guarantees a
.cmdthat 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/— cleantest_project/in Godot on a Windows host with npm-installedclaudetest_run suite=cli_finder—_pick_best_pathinvariants pass (npm reproducer,.exe>.cmd, CRLF endings, fallback, blanks, empty input)test_run suite=cli_exec— the new Windows.cmdsmoke captures stdout through thecmd.exewrap; existing Unix-side tests still passtest_project/in Godot on macOS or Linuxtest_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 unchangedhttps://claude.ai/code/session_01C5WkB2x3RmUCmhzdnYzEes
Generated by Claude Code