Skip to content

Fix Windows .bat/.cmd shim spawn failure in copilotSdk.ts by adding early-exit to external server mode#53

Merged
digitarald merged 4 commits intomicrosoft:mainfrom
nicolehaugen:copilot/glad-kiwi
Mar 15, 2026
Merged

Fix Windows .bat/.cmd shim spawn failure in copilotSdk.ts by adding early-exit to external server mode#53
digitarald merged 4 commits intomicrosoft:mainfrom
nicolehaugen:copilot/glad-kiwi

Conversation

@nicolehaugen
Copy link
Member

@nicolehaugen nicolehaugen commented Mar 12, 2026

Summary

##Repro:
1.) Run: agentrc instructions on Windows machine when underlying call to copilot sdk is resolved from VS Code global Storage as copilot.bat
2.) Error:
Generating instructions...
[agentrc:copilot] trying override AGENTRC_COPILOT_CLI_PATH=C:\Users\nicolela\AppData\Roaming\Code\User\globalStorage\github.copilot-chat\copilotCli\copilot.bat
[agentrc:copilot] override CLI is compatible
[agentrc:copilot] validating CLI compatibility with C:\Users\nicolela\AppData\Roaming\Code\User\globalStorage\github.copilot-chat\copilotCli\copilot.bat
[agentrc:copilot] creating SDK client with cliPath=C:\Users\nicolela\AppData\Roaming\Code\User\globalStorage\github.copilot-chat\copilotCli\copilot.bat useStdio=false
Error: Failed to generate instructions with Copilot SDK. Ensure the Copilot CLI is installed (copilot --version) and logged in. spawn EINVAL

Problem

On Windows, when @github/copilot is not installed globally via npm, the Copilot CLI discovery chain in findCopilotCliConfig() (in copilot.ts) resolves to a .bat shim at:

%APPDATA%\Code\User\globalStorage\github.copilot-chat\copilotCli\copilot.bat

This is step 3 of the 6-step discovery chain:

  1. AGENTRC_COPILOT_CLI_PATH env var override
  2. npm global install (node.exe + npm-loader.js) — no .bat issue
  3. VS Code globalStoragecopilot.bat on Windows ← this is where the problem occurs
  4. where copilot (PATH lookup)
  5. npx
  6. VS Code extensions glob

When a user has Copilot only through the VS Code extension (not npm-installed), discovery lands on step 3 and returns { cliPath: "...\\copilot.bat" }.

Node.js child_process.spawn() calls the Windows CreateProcess API, which can only execute PE binaries (.exe, .dll). A .bat file is a script that needs cmd.exe as its interpreter. Spawning it directly produces:

Error: spawn EINVAL

The SDK-managed primary attempt always fails on this path. Before this fix, it would:

  1. Try SDK-managed mode → fail with spawn EINVAL or Copilot CLI not found at cmd
  2. Fall back to external server mode (which wraps the .bat in cmd /c)

This wasteful "sacrificial" primary attempt adds latency on every invocation.

How this situation occurs

A developer installs VS Code with the GitHub Copilot Chat extension. The extension downloads the Copilot CLI to its globalStorage as copilot.bat. If the developer also runs npm install -g @github/copilot, discovery resolves at step 2 to node.exe + npm-loader.js (a real PE binary), and there's no issue. But if they only have the VS Code extension, step 2 fails and step 3 returns the .bat shim.

Solution

Two changes in packages/core/src/services/copilotSdk.ts:

  1. Early-exit for .bat/.cmd shims (alongside existing npx early-exit) - In createCopilotClient(), detect .bat/.cmd paths on Windows and skip the SDK-managed primary attempt entirely — go straight to external server mode, just like the existing isNpx check.
  2. Safety-net in shouldFallbackToExternalServer() - Add "spawn einval" as a recognized fallback trigger so that if the early-exit is somehow bypassed (e.g. a future code path), the error is still caught.

Checklist

  • [NA ] Tests added or updated
  • [X ] Lint/typecheck pass
  • [NA ] Docs updated if needed

nicolehaugen and others added 4 commits March 12, 2026 16:47
…ver mode

Co-authored-by: nicolehaugen <10600161+nicolehaugen@users.noreply.github.com>
…im, revert test scope

Co-authored-by: nicolehaugen <10600161+nicolehaugen@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 12, 2026 23:54
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Improves Windows compatibility for Copilot SDK startup by avoiding SDK-managed spawning when the discovered Copilot CLI path is a Windows .bat/.cmd shim (which can fail with spawn EINVAL), and instead going directly to the existing external server mode.

Changes:

  • Add .bat/.cmd shim detection (Windows-only) to skip the SDK-managed startup path and immediately use external server mode.
  • Treat spawn EINVAL as a recognized trigger for falling back to external server mode (as a safety net).

@digitarald digitarald merged commit e3e835b into microsoft:main Mar 15, 2026
13 of 14 checks passed
github-actions bot pushed a commit that referenced this pull request Mar 15, 2026
- Add [Unreleased] section documenting features from PRs #55, #60, #53, #52:
  - instructions --dry-run flag
  - VS Code batch instructions and multi-root workspace support
  - Enhanced .NET/F# detection with framework parsing
  - Windows .bat/.cmd spawn EINVAL fix
  - ESM/CJS createRequire banner fix
  - Model default updated to claude-sonnet-4.6
  - TUI key binding changes (R for readiness, N for nested area)
  - Terminology de-branding
- Fix stale claude-sonnet-4.5 reference in [2.0.0] section

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

4 participants