claude-code - 💡(How to fix) Fix LSP plugins fail to spawn npm-installed language servers on Windows (.cmd wrappers not resolved by uv_spawn) [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#54650Fetched 2026-04-30 06:39:50
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
0
Author
Timeline (top)
labeled ×5commented ×1

On Windows, LSP plugins (e.g., typescript-lsp) fail to spawn language-server binaries that npm installs as .cmd shims. The LSP tool returns:

Error performing <op>: ENOENT: no such file or directory, uv_spawn 'typescript-language-server'

even when where typescript-language-server resolves to a valid .cmd and the binary runs fine from a normal shell.

Error Message

Error performing <op>: ENOENT: no such file or directory, uv_spawn 'typescript-language-server'

Root Cause

On Windows, npm global installs only create .cmd / .ps1 / extension-less shims for binaries (no real .exe). libuv's uv_spawn, when called without a shell, does not search PATHEXT for .cmd / .bat — it only resolves .exe. Same caveat applies to Node's child_process.spawn with shell: false. Linux/macOS work because npm writes a real executable shim.

Fix Action

Workaround

Built a tiny PE shim via Add-Type -OutputType ConsoleApplication:

using System;
using System.Diagnostics;
class L {
    static int Main(string[] a) {
        var psi = new ProcessStartInfo {
            FileName = "node.exe",
            UseShellExecute = false
        };
        psi.Arguments = "\"<path-to-cli.mjs>\"";
        foreach (var x in a) psi.Arguments += " \"" + x.Replace("\"","\\\"") + "\"";
        var p = Process.Start(psi);
        p.WaitForExit();
        return p.ExitCode;
    }
}

Dropping the resulting typescript-language-server.exe next to the .cmd in %APPDATA%\npm\ resolves it (PATHEXT prefers .EXE over .CMD).

Code Example

Error performing <op>: ENOENT: no such file or directory, uv_spawn 'typescript-language-server'

---

using System;
using System.Diagnostics;
class L {
    static int Main(string[] a) {
        var psi = new ProcessStartInfo {
            FileName = "node.exe",
            UseShellExecute = false
        };
        psi.Arguments = "\"<path-to-cli.mjs>\"";
        foreach (var x in a) psi.Arguments += " \"" + x.Replace("\"","\\\"") + "\"";
        var p = Process.Start(psi);
        p.WaitForExit();
        return p.ExitCode;
    }
}
RAW_BUFFERClick to expand / collapse

Summary

On Windows, LSP plugins (e.g., typescript-lsp) fail to spawn language-server binaries that npm installs as .cmd shims. The LSP tool returns:

Error performing <op>: ENOENT: no such file or directory, uv_spawn 'typescript-language-server'

even when where typescript-language-server resolves to a valid .cmd and the binary runs fine from a normal shell.

Repro

  1. Windows 10/11. Native Node (e.g. v24.11.1) installed at C:\Program Files\nodejs.
  2. npm install -g typescript-language-server typescript
  3. Enable plugin typescript-lsp@claude-plugins-official in ~/.claude/settings.json.
  4. Call any LSP operation (documentSymbol, hover, workspaceSymbol, ...) on a .ts file.

Expected: LSP returns symbol info. Actual: uv_spawn 'typescript-language-server' fails with ENOENT.

Root cause

On Windows, npm global installs only create .cmd / .ps1 / extension-less shims for binaries (no real .exe). libuv's uv_spawn, when called without a shell, does not search PATHEXT for .cmd / .bat — it only resolves .exe. Same caveat applies to Node's child_process.spawn with shell: false. Linux/macOS work because npm writes a real executable shim.

Workaround

Built a tiny PE shim via Add-Type -OutputType ConsoleApplication:

using System;
using System.Diagnostics;
class L {
    static int Main(string[] a) {
        var psi = new ProcessStartInfo {
            FileName = "node.exe",
            UseShellExecute = false
        };
        psi.Arguments = "\"<path-to-cli.mjs>\"";
        foreach (var x in a) psi.Arguments += " \"" + x.Replace("\"","\\\"") + "\"";
        var p = Process.Start(psi);
        p.WaitForExit();
        return p.ExitCode;
    }
}

Dropping the resulting typescript-language-server.exe next to the .cmd in %APPDATA%\npm\ resolves it (PATHEXT prefers .EXE over .CMD).

Suggested fix (any of)

  • Spawn LSP servers with shell: true on Windows.
  • Resolve via where.exe <name> first and call the absolute path.
  • Allow plugin authors to override the spawn command via plugin manifest (e.g. a command / args field in the plugin descriptor).

Affected plugins

Likely all *-lsp plugins on Windows that rely on npm-installed launchers (typescript-lsp, possibly others depending on how their language server distributes Windows entrypoints).

Environment

  • OS: Windows 10 Enterprise 19045
  • Node: v24.11.1 (native install at C:\Program Files\nodejs)
  • typescript-language-server: 5.1.3
  • Claude Code: latest as of 2026-04-29

extent analysis

TL;DR

Spawn LSP servers with shell: true on Windows to resolve the ENOENT error when using npm-installed language server binaries with .cmd shims.

Guidance

  • Set shell: true when calling uv_spawn or child_process.spawn to enable searching PATHEXT for .cmd files on Windows.
  • Use the where.exe command to resolve the absolute path of the language server binary before spawning it.
  • Consider allowing plugin authors to override the spawn command via plugin manifest to provide more flexibility.

Example

const childProcess = require('child_process');
// ...
const spawnOptions = {
  shell: true, // Enable shell execution to search PATHEXT
};
childProcess.spawn('typescript-language-server', [], spawnOptions);

Notes

This solution assumes that the language server binary is installed correctly and the .cmd shim is in the system's PATH. The shell: true option may introduce security risks if the command or arguments are not properly sanitized.

Recommendation

Apply workaround: Spawn LSP servers with shell: true on Windows, as it is a straightforward solution that can be implemented without modifying the plugin or language server code.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING