ENABLE_LSP_TOOL: LSP subprocess stdin is immediately EOF, server shuts down before receiving any LSP messages

Resolved 💬 1 comment Opened Apr 27, 2026 by aviadshiber Closed May 29, 2026

Summary

When ENABLE_LSP_TOOL=1 is set and an LSP server is configured via .lsp.json or plugin.json, Claude Code starts the server subprocess but closes the stdin pipe immediately — before sending the LSP initialize request. The server receives EOF on first readline() and shuts down.

Reproduction

Configure any pygls-based LSP server (e.g. java-functional-lsp) in .lsp.json:

{
  "servers": [{
    "name": "my-server",
    "command": ["/path/to/server"],
    "languages": ["java"],
    "extensionToLanguage": {".java": "java"}
  }]
}

Invoke any LSP tool (e.g. documentSymbol). Result: "No LSP server available for file type: .java".

Server log (/tmp/java-functional-lsp.log):

=== java-functional-lsp started at 2026-04-27T08:17:55Z ===
pygls.server INFO: Starting async IO server
pygls.server INFO: Shutting down the server

No initialize is logged between start and shutdown — the server exits before receiving any messages.

Root Cause Diagnosis

The server only shuts down when stdin receives EOF. Manual tests confirm:

# stdin = /dev/null → immediate shutdown (same as Claude Code's behavior)
binary </dev/null            # "Starting async IO server" → "Shutting down"

# stdin = open pipe (empty) → server waits indefinitely ✅
sleep 3 | binary             # "Starting async IO server" (waits, no shutdown)

# stdin = proper LSP-framed request → server responds correctly ✅
printf "Content-Length: %d\r\n\r\n%s" ${#MSG} "$MSG" | binary  # works

This means Claude Code provides stdin as EOF (either stdio: 'ignore' for stdin, or closes the write-end of the pipe immediately after spawning the process). No LSP handshake (initialize / initialized) is ever sent.

Expected Behavior

Claude Code should keep the write-end of the stdin pipe open for the lifetime of the LSP server session, and send the standard LSP initialize request before making any tool calls.

Actual Behavior

  • Server starts and immediately shuts down on stdin EOF
  • documentSymbol, goToDefinition, hover, etc. all return "No LSP server available"
  • The server startup is cached as failed; no retry within the session

Environment

  • Claude Code with ENABLE_LSP_TOOL=1
  • .lsp.json project-level configuration
  • java-functional-lsp 0.9.17 (pygls-based)
  • macOS 25.3.0 / Python 3.10.18

Workaround

Until fixed upstream, java-functional-lsp 0.9.18+ wraps sys.stdin.buffer with an _EternalStdinBuffer that blocks forever on EOF instead of propagating it, and start-lsp.sh routes stdin through a FIFO with a keepalive writer (tail -f /dev/null).

🤖 Diagnosed with Claude Code

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗