typescript-lsp plugin ENOENT on Windows: spawn without shell can't resolve .cmd shims

Resolved 💬 3 comments Opened Apr 4, 2026 by LZong-tw Closed Apr 8, 2026

Summary

The typescript-lsp plugin fails on Windows with ENOENT: no such file or directory, uv_spawn 'typescript-language-server' despite the binary being correctly installed and on PATH.

Environment

  • Claude Code: latest (desktop app, Windows)
  • Windows 11 Pro 10.0.26200
  • Node.js v24.14.0 (via nvm4w)
  • typescript-language-server 5.1.3 installed globally (npm i -g typescript-language-server typescript)

Reproduction

  1. Windows machine, install typescript-language-server globally
  2. Verify it works: typescript-language-server --version5.1.3
  3. Enable typescript-lsp@claude-plugins-official plugin
  4. Start a Claude Code session
plugin:typescript-lsp:typescript @ unknown (user)
ENOENT: no such file or directory, uv_spawn 'typescript-language-server'

Root Cause

On Windows, npm i -g creates .cmd shim files (batch wrappers):

C:\nvm4w\nodejs\typescript-language-server      ← shell script (bash)
C:\nvm4w\nodejs\typescript-language-server.cmd   ← Windows batch wrapper
C:\nvm4w\nodejs\typescript-language-server.ps1   ← PowerShell wrapper

child_process.spawn('typescript-language-server') without { shell: true } looks for an exact executable named typescript-language-server (or .exe). On Windows, it does NOT resolve .cmd shims. The spawn fails with ENOENT.

In bash (Git Bash/MINGW64), the shell script version works fine. But Node.js spawn bypasses the shell entirely.

Fix

Add { shell: true } when spawning language servers on Windows, or use cross-spawn logic to resolve .cmd extensions.

const opts = {};
if (process.platform === 'win32') opts.shell = true;
child_process.spawn('typescript-language-server', ['--stdio'], opts);

Workaround

Users can disable typescript-lsp plugin if they have Serena or another LSP provider.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗