Description
The LSP client fails to spawn any npm-installed language server (e.g., intelephense, typescript-language-server) on Windows with:
OSError: [WinError 193] %1 is not a valid Win32 application
Root Cause
When you npm install -g <package>, npm creates three files on Windows:
<bin> — a shell script (for MSYS/Git Bash)
<bin>.cmd — a batch file wrapper (for cmd.exe)
<bin>.ps1 — a PowerShell script (for PowerShell)
Python's shutil.which() resolves to the .cmd variant (via PATHEXT), but asyncio.create_subprocess_exec() passes it directly to Windows' CreateProcess() API. CreateProcess expects a valid PE executable — .cmd files are batch scripts that need cmd.exe /c to interpret them, so it fails with WinError 193.
Why Linux/macOS Is Unaffected
On Linux and macOS, npm creates shell scripts with a shebang (#!/usr/bin/env node) and sets the execute permission bit. When create_subprocess_exec runs one, the OS kernel reads the shebang and routes it to the Node interpreter automatically. Windows has no shebang mechanism — it relies on file extensions and CreateProcess, which can only natively run PE executables (.exe). This is why the issue is Windows-specific.
Affected Servers
Any LSP server installed via npm, including:
intelephense (PHP)
typescript-language-server / tsserver (TypeScript/JavaScript)
vscode-langservers-extracted (HTML/CSS/JSON/ESLint)
svelte-language-server
- Any other npm-installed LSP binary
Environment
- OS: Windows 10 (Build 26200)
- Python: 3.11.15
- Node.js: v24.14.0
- npm: 11.9.0
- Hermes Agent: Windows native (not WSL)
Steps to Reproduce
- Install any npm-based LSP server:
npm install -g intelephense
- Open a project that triggers that LSP (e.g., a PHP/Laravel project)
- LSP manager attempts to spawn the server
asyncio.create_subprocess_exec fails with WinError 193
Proposed Fix
In agent/lsp/client.py, detect Windows .cmd/.bat shims and wrap them with cmd.exe /c before passing to create_subprocess_exec:
@staticmethod
def _win_wrap_cmd(cmd: list[str]) -> list[str]:
"""On Windows, wrap .cmd/.bat shims so CreateProcess can run them."""
exe = cmd[0]
if exe.lower().endswith((".cmd", ".bat")):
return ["cmd.exe", "/c", *cmd]
return cmd
Then in _spawn():
cmd = self._command
if sys.platform == "win32":
cmd = self._win_wrap_cmd(cmd)
Minimal, backward-compatible fix — non-Windows platforms are unaffected, and native .exe binaries pass through untouched.
Description
The LSP client fails to spawn any npm-installed language server (e.g.,
intelephense,typescript-language-server) on Windows with:Root Cause
When you
npm install -g <package>, npm creates three files on Windows:<bin>— a shell script (for MSYS/Git Bash)<bin>.cmd— a batch file wrapper (for cmd.exe)<bin>.ps1— a PowerShell script (for PowerShell)Python's
shutil.which()resolves to the.cmdvariant (viaPATHEXT), butasyncio.create_subprocess_exec()passes it directly to Windows'CreateProcess()API.CreateProcessexpects a valid PE executable —.cmdfiles are batch scripts that needcmd.exe /cto interpret them, so it fails withWinError 193.Why Linux/macOS Is Unaffected
On Linux and macOS, npm creates shell scripts with a shebang (
#!/usr/bin/env node) and sets the execute permission bit. Whencreate_subprocess_execruns one, the OS kernel reads the shebang and routes it to the Node interpreter automatically. Windows has no shebang mechanism — it relies on file extensions andCreateProcess, which can only natively run PE executables (.exe). This is why the issue is Windows-specific.Affected Servers
Any LSP server installed via npm, including:
intelephense(PHP)typescript-language-server/tsserver(TypeScript/JavaScript)vscode-langservers-extracted(HTML/CSS/JSON/ESLint)svelte-language-serverEnvironment
Steps to Reproduce
npm install -g intelephenseasyncio.create_subprocess_execfails withWinError 193Proposed Fix
In
agent/lsp/client.py, detect Windows.cmd/.batshims and wrap them withcmd.exe /cbefore passing tocreate_subprocess_exec:Then in
_spawn():Minimal, backward-compatible fix — non-Windows platforms are unaffected, and native
.exebinaries pass through untouched.