-
Notifications
You must be signed in to change notification settings - Fork 156
Description
Environment
- OS: Windows 11
- Python: 3.12.10
- Package:
github-copilot-sdkv0.1.14 - Copilot CLI Version: 0.0.388
Description
On Windows, the CopilotClient fails to start with FileNotFoundError: [WinError 2] The system cannot find the file specified when the copilot CLI is installed as a .cmd or .bat file (which is standard for npm global installs on Windows).
Steps to Reproduce
- Install
copilotCLI via npm on Windows:npm install -g @anthropic/copilot-cli - Verify it's on PATH:
where copilotreturnsC:\.tools\.npm\prefix\copilot.cmd - Use the Python SDK:
from copilot import CopilotClient
async def main():
client = CopilotClient()
await client.start() # Raises WinError 2Expected Behavior
The SDK should find and execute the copilot CLI when it's on PATH, regardless of file extension.
Actual Behavior
FileNotFoundError: [WinError 2] The system cannot find the file specified
Full traceback:
File "...\copilot\client.py", line 645, in _start_cli_server
self._process = subprocess.Popen(
^^^^^^^^^^^^^^^^^
File "...\subprocess.py", line 1026, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "...\subprocess.py", line 1538, in _execute_child
hp, ht, pid, tid = _winapi.CreateProcess(executable, args,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [WinError 2] The system cannot find the file specified
NOTE: Bellow is generated by copilot, so who even knows. Also for some reason it worked for a bit then it stopped and weirdly enough my VSCode has some arcane issues with path which it (maybe) passed down on it's child processes started from its terminal. So maybe it's all a big misunderstanding.
Root Cause
In client.py line ~645, subprocess.Popen is called with the command name directly:
self._process = subprocess.Popen(
args, # args[0] is "copilot" or cli_path
...
)On Windows, subprocess.Popen without shell=True:
- Does not use
PATHEXTto resolve extensions (.cmd,.bat,.exe) - Cannot directly execute
.cmd/.batfiles by name alone - Does not behave like a shell when searching PATH
Workaround
Users can work around this by using shutil.which() to resolve the full path:
import shutil
from copilot import CopilotClient
cli_path = shutil.which("copilot") # Returns full path with extension
client = CopilotClient({"cli_path": cli_path})
await client.start() # WorksSuggested Fix
In client.py, before calling subprocess.Popen, use shutil.which() to resolve the CLI path:
import shutil
async def _start_cli_server(self) -> None:
cli_path = self.options["cli_path"]
# Resolve the full path on Windows (handles .cmd/.bat files)
resolved_path = shutil.which(cli_path)
if resolved_path:
cli_path = resolved_path
args = ["--server", "--log-level", self.options["log_level"]]
# ... rest of the codeAlternatively, on Windows, use shell=True when the cli_path doesn't contain a path separator (indicating it needs PATH resolution).
Additional Context
This affects any Windows user where copilot is installed via npm, as npm creates .cmd wrapper scripts for global packages.