[BUG] Claude Code - LSP Tool Returns Empty Results Despite Server Responding Correctly (Windows)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Two issues with LSP Tool on Windows:
Issue #1 - ENOENT Error: Claude Code cannot spawn LSP servers because Node.js spawn() without shell: true cannot find .cmd or .ps1 files on Windows. Error: spawn typescript-language-server ENOENT
Issue #2 - Empty Results Bug: After creating .exe shims to fix Issue #1, the LSP tool returns "No symbols found", "No hover information", etc. for ALL document-level operations, even though the LSP server responds correctly with valid data. Only workspaceSymbol works.
This affects ALL tested LSP servers including the official typescript-lsp@claude-plugins-official plugin.
What Should Happen?
- LSP servers should spawn correctly on Windows (find
.cmdfiles or useshell: true) - Document-level operations (
documentSymbol,hover,findReferences,goToDefinition, etc.) should return the data that the LSP server provides
Direct test results (same file, same LSP server):
| Operation | Direct Test | Claude Code |
|-----------|------------|-------------|
| documentSymbol | ✅ 6 items | ❌ "No symbols found" |
| hover | ✅ "class UserService" | ❌ "No hover info" |
| references | ✅ 7 items | ❌ "No references" |
| workspaceSymbol | ✅ 256 items | ✅ 13 items |
Error Messages/Logs
**Issue #1 - ENOENT (before .exe shim fix):**
[ERROR] LSP server plugin:typescript-lsp:typescript failed to start: spawn typescript-language-server ENOENT
[ERROR] LSP server plugin:vtsls:typescript failed to start: spawn vtsls ENOENT
**Issue #2 - Server responds but Claude Code shows empty results:**
[DEBUG] [LSP PROTOCOL plugin:vtsls:typescript] Sending notification 'textDocument/didOpen'.
[DEBUG] LSP: Sent didOpen for C:\...\test-lsp.ts (languageId: typescript)
[DEBUG] [LSP PROTOCOL plugin:vtsls:typescript] Sending request 'textDocument/hover - (1)'.
[DEBUG] [LSP PROTOCOL plugin:vtsls:typescript] Received response 'textDocument/hover - (1)' in 4ms. ✅
[DEBUG] [LSP PROTOCOL plugin:vtsls:typescript] Sending request 'textDocument/documentSymbol - (2)'.
[DEBUG] [LSP PROTOCOL plugin:vtsls:typescript] Received response 'textDocument/documentSymbol - (2)' in 32ms. ✅
[DEBUG] [LSP PROTOCOL plugin:vtsls:typescript] Sending request 'textDocument/references - (3)'.
[DEBUG] [LSP PROTOCOL plugin:vtsls:typescript] Received response 'textDocument/references - (3)' in 61ms. ✅
**User sees:** "No symbols found", "No hover information available", "No references found"
Steps to Reproduce
Issue #1 (ENOENT):
- Fresh Windows 10/11 install
- Install Node.js and run:
npm install -g typescript-language-server typescript - In Claude Code settings, add:
"env": { "ENABLE_LSP_TOOL": "1" } - Enable
typescript-lsp@claude-plugins-officialplugin - Restart Claude Code
- Check debug logs at
~/.claude/debug/*.txt - Observe:
spawn typescript-language-server ENOENT
Issue #2 (Empty Results - after fixing Issue #1):
- Create
.exeshims for LSP servers using PowerShell:
$code = @"
using System;
using System.Diagnostics;
public class Shim {
public static int Main(string[] args) {
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.Arguments = "/c typescript-language-server.cmd " + string.Join(" ", args);
info.UseShellExecute = false;
Process p = Process.Start(info);
p.WaitForExit();
return p.ExitCode;
}
}
"@
Add-Type -TypeDefinition $code -OutputAssembly "C:\Users\USERNAME\AppData\Roaming\npm\typescript-language-server.exe" -OutputType ConsoleApplication
- Create test file
test-lsp.ts:
interface User {
id: number;
name: string;
}
class UserService {
private users: User[] = [];
addUser(user: User): void {
this.users.push(user);
}
}
function greet(user: User): string {
return `Hello, ${user.name}!`;
}
const service = new UserService();
- Restart Claude Code
- Use LSP tool:
documentSymbolon test-lsp.ts - Observe: "No symbols found in document"
- Use LSP tool:
workspaceSymbolon test-lsp.ts - Observe: Returns symbols correctly (this one works!)
- Check debug logs: Server received response in 32ms but user sees empty results
---
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.3
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
Windows Terminal
Additional Information
_No response_
Showing cached comments. Read the full discussion on GitHub ↗
14 Comments
Additional Findings (Windows + npm installation)
### Working vs Non-Working LSP Methods
After further testing on Windows with npm-installed Claude Code, I found that only specific LSP methods are working:
| Method | Type | Status |
|--------|------|--------|
|
workspace/symbol| Request/Response | ✅ Working ||
textDocument/publishDiagnostics| Server → Client Push | ✅ Working ||
textDocument/hover| Request/Response | ❌ Not working ||
textDocument/references| Request/Response | ❌ Not working ||
textDocument/definition| Request/Response | ❌ Not working ||
textDocument/documentSymbol| Request/Response | ❌ Not working ||
callHierarchy/incomingCalls| Request/Response | ❌ Not working ||
callHierarchy/outgoingCalls| Request/Response | ❌ Not working |### Key Observations
.jsand.tsfilesjsconfig.jsondidn't change resultsworkspaceSymbolanddiagnosticsappear to be implemented/workingworkspaceSymbolis also request/response but works fine### Environment
npm install -g @anthropic-ai/claude-code)I can confirm this issue on Windows 11 (Build 10.0.26200.7628) with Claude Code 2.1.31.
Test Results:
| Operation | macOS | Windows |
|-----------|-------|---------|
|
workspaceSymbol| ✅ Works | ✅ Works ||
documentSymbol| ✅ Works | ❌ No symbols found ||
goToDefinition| ✅ Works | ❌ No definition found ||
hover| ✅ Works | ⚠️ No output ||
findReferences| ✅ Works | ❌ No references found ||
goToImplementation| ✅ Works | ❌ No definition found ||
incomingCalls| ✅ Works | ❌ No call hierarchy item found ||
outgoingCalls| ✅ Works | ❌ No call hierarchy item found |Observations:
workspaceSymbolis the only operation that works correctly on Windowstypescript-language-serve) is confirmed running via Task Managertsconfig.jsonandnode_modulesinstalledPossible Causes:
tsconfig.jsonStill reproduces as of 2.1.45
Root Cause Found & Full Workaround (TypeScript + Python LSP)
After building a JSON-RPC logging proxy between Claude Code and the LSP server, I identified the root cause of Issue #2 (empty results):
Claude Code's LSP client does NOT send
textDocument/didOpenbefore document-level requests on Windows.Per the LSP specification, the server must receive
textDocument/didOpen(containing the full source text) before it can respond tohover,definition,references,documentSymbol, etc. Without it, the server returns empty results withUnexpected resourceerrors internally. This is whyworkspaceSymbolworks — it doesn't require a specific document to be opened.---
How the Proxy Fixes It
The proxy sits between Claude Code and the real LSP server, doing two things:
file://C:\Users\...(backslash, 2 slashes). The proxy rewrites all URIs tofile:///C:/Users/...(forward slash, 3 slashes) per the URI spec.textDocument/didOpen— When a document-level request (hover, definition, etc.) arrives for a file not yet opened, the proxy reads the file from disk and sendsdidOpento the server before forwarding the original request.Architecture & Flow
Why a C#
.exeshim? Claude Code uses Node.jsspawn()withoutshell: true, so it cannot find.cmd/.ps1files on Windows (Issue #1). A native.exein PATH solves the ENOENT problem. The shim extracts its own filename to pass--server-nameto the proxy, so one source file builds all shims.File Structure
Test Results
TypeScript (typescript-language-server):
| Operation | Before Proxy | After Proxy |
|-----------|:---:|:---:|
| hover | ❌ empty | ✅ type info |
| goToDefinition | ❌ empty | ✅ correct file:line |
| findReferences | ❌ empty | ✅ all refs found |
| documentSymbol | ❌ empty | ✅ full symbol tree |
| diagnostics | ✅ | ✅ |
| workspaceSymbol | ✅ | ✅ |
Python (pyright-langserver):
| Operation | Before Proxy | After Proxy |
|-----------|:---:|:---:|
| hover | ❌ empty | ✅ function signature + docstring |
| goToDefinition | ❌ empty | ✅ jumps to typeshed/source |
| findReferences | ❌ empty | ✅ all refs found |
| documentSymbol | ❌ empty | ✅ functions + variables |
---
Setup Instructions
Step 0: PATH Configuration (Critical)
Open Start → Run →
sysdm.cpl→ Advanced → Environment Variables → User variables → Path, and move%USERPROFILE%\.local\binto the top of the list. This ensures Claude Code finds our shim.exefiles before any.cmdwrappers in npm/bun directories.Step 1: Create directory structure
Step 2: Create
%USERPROFILE%\.local\bin\lsp-proxy\typescript-language-server.csThis is the universal shim source. It detects its own
.exefilename and passes it as--server-nameto the proxy.Step 3: Create
%USERPROFILE%\.local\bin\lsp-proxy\lsp-proxy.js<details>
<summary>Click to expand — lsp-proxy.js (full source)</summary>
</details>
Step 4: Compile the shims
Both
.exefiles are built from the same.cssource — only the output filename differs:Step 5: Update
SERVER_MAPpaths inlsp-proxy.jsThe
SERVER_MAPobject maps server names to the real entry script. Update paths based on your package manager:Or override per-server with environment variable:
set LSP_REAL_SERVER=C:\path\to\server.jsStep 6: Restart Claude Code
The proxy starts automatically when Claude Code spawns an LSP server. You should see the shim processes in Task Manager.
Logs are written to
%USERPROFILE%\.local\bin\lsp-proxy\lsp-proxy-YYYY-MM-DD.jsonl.---
Note for the Claude Code team
The proper fix should be straightforward: send
textDocument/didOpenbefore any document-level LSP request on Windows. The proxy workaround confirms this is the only missing piece — oncedidOpenis sent, all LSP operations work correctly. Additionally, normalizingfile://URIs for Windows paths would prevent theUnexpected resourceerrors.Tested on Windows 10 Pro (10.0.19045) with Claude Code 2.1.47+, typescript-language-server 5.1.3, and pyright 1.1.x.
Still reproduces as of 2.1.50
Hey! I ran into a similar pattern in our bug knowledge base and thought this might help.
What's happening: Two distinct Windows-specific bugs in Claude Code's LSP client: (1) Node.js spawn() is called without shell:true, so it cannot locate .cmd/.ps1 shims that npm/yarn create for LSP server binaries on Windows (e.g., typescript-language-server.cmd). (2) After resolving the spawn issue, Claude Code's LSP client does NOT send textDocument/didOpen notifications before issuing document-level requests (hover, documentSymbol, references, goToDefinition). Per the LSP specification, the server MUST receive didOpen for a document before it can respond to any document-scoped requests. workspace/symbol works because it is not document-scoped. This is a Windows-only regression because on macOS/Linux the LSP binary is found directly and the didOpen lifecycle apparently works correctly.
What worked for us:
The proposed fix correctly identifies the need for
textDocument/didClosenotifications to prevent memory leaks in the LSP server, but the provided code snippet for the fix does not include this. An improved fix would explicitly show how and when to sendtextDocument/didClosefor a document no longer in use, perhaps when a tab is closed or the editor session ends for that file. Additionally, forgetLanguageId(filePath), a common implementation would involve a map of file extensions to LSP language IDs (e.g.,.ts->typescript,.js->javascript,.py->python).📊 _We found 5 similar cases in our knowledge base with the same pattern — this gives us high confidence in this analysis._
Hope this helps! Let me know if it doesn't match your case — happy to dig deeper. 🦞
---
<sub>🦞 Confucius Debug — community knowledge base for AI agent bugs. Free to search via MCP.</sub>
still reproduces as of v2.1.71
Still reproducing on v2.1.87
still reproducing in v2.1.97
resolved in v2.1.132
최신 버전에서 수정 확인됨. proxy shim 없이 LSP hover/definition/references 정상 동작 확인.
Confirmed Fixed in v2.1.132+
Can confirm the fix works on my end. Tested on Windows 10 Pro (10.0.19045) with Claude Code v2.1.132+.
What I tested
After the fix landed, I removed the proxy shim workaround I had built (the C#
.exeshims +lsp-proxy.jsthat auto-injectedtextDocument/didOpen) and retested all three LSP servers from scratch:| Server | Operation | Before Fix | After Fix |
|--------|-----------|:---:|:---:|
| typescript-language-server |
documentSymbol| ❌ empty | ✅ full symbol tree || typescript-language-server |
hover| ❌ empty | ✅ type info || typescript-language-server |
goToDefinition| ❌ empty | ✅ correct file:line || typescript-language-server |
findReferences| ❌ empty | ✅ all refs found || pyright-langserver |
documentSymbol| ❌ empty | ✅ functions + variables || pyright-langserver |
hover| ❌ empty | ✅ signature + docstring || pyright-langserver |
goToDefinition| ❌ empty | ✅ jumps to typeshed || pyright-langserver |
findReferences| ❌ empty | ✅ all refs found || rust-analyzer |
documentSymbol| ❌ (not tested before) | ✅ full symbol tree |All document-level LSP methods now work correctly without any workaround. The proxy shim is no longer needed and has been removed.
Root cause recap
The issue was that Claude Code's LSP client was not sending
textDocument/didOpenbefore document-level requests on Windows, causing the LSP server to return empty results withUnexpected resourceerrors internally. TheworkspaceSymbolmethod worked because it doesn't require a document to be opened first. The fix in v2.1.132 ensuresdidOpenis properly sent, making all document-scoped operations functional.Thanks to the team for the quick fix, and to @vino24 for flagging the version!
This is not fixed for workspaceSymbol using jdtls on windows, all other LSP tools work in jdtls using Claude Code v2.1.138 except workspaceSymbol.
Still getting ENOENT in v2.1.161 with official marketplace plugins
Environment: Windows 11 Home (10.0.26200), Claude Code v2.1.161, both LSP servers installed via npm globally.
While the
textDocument/didOpenfix (Issue #2) is confirmed working, the ENOENT spawn issue (Issue #1) still affects users of the official marketplace plugins (php-lsp@claude-plugins-official,typescript-lsp@claude-plugins-official):Root cause: the official
marketplace.jsonstill specifies bare command names without the.cmdextension:Manually patching these to
"intelephense.cmd"/"typescript-language-server.cmd"fixes the spawn immediately. So the underlying LSP implementation is fine — the marketplace plugin config just hasn't been updated for Windows.Could the official plugin definitions be updated to use
.cmdon Windows, or could Claude Code fall back to.cmdautomatically when a bare command returns ENOENT on Windows?