Skip to content

LSP spawn fails on Windows with WinError 193 for npm-installed language servers #34864

@tuancookiez-hub

Description

@tuancookiez-hub

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

  1. Install any npm-based LSP server: npm install -g intelephense
  2. Open a project that triggers that LSP (e.g., a PHP/Laravel project)
  3. LSP manager attempts to spawn the server
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havecomp/agentCore agent loop, run_agent.py, prompt buildertype/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions