NousResearch/hermes-agent · error · LSPProtocolError
LSP server binary not found: {cmd[0]} ({e})
Error message
LSP server binary not found: {cmd[0]} ({e}) What it means
LSPProtocolError raised when asyncio.create_subprocess_exec fails with FileNotFoundError while spawning the language server — the configured server command's executable does not exist on PATH (or is not executable). Raised during client startup before any LSP handshake happens.
Source
Thrown at agent/lsp/client.py:326
# process group / session. Without this, the LSP server inherits
# the gateway's pgid (= TUI parent PID). When mcp_tool's
# _kill_orphaned_mcp_children races with LSP spawn and sweeps the
# gateway's child set, it captures the LSP PID, records the
# inherited pgid, and killpg() then kills the TUI parent itself.
# See tui_gateway_crash.log "killpg → SIGTERM received" stacks.
self._proc = await asyncio.create_subprocess_exec(
cmd[0],
*cmd[1:],
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
env=env,
cwd=self._cwd,
start_new_session=True,
creationflags=creationflags,
)
except FileNotFoundError as e:
raise LSPProtocolError(
f"LSP server binary not found: {cmd[0]} ({e})"
) from e
# Drain stderr at debug level — if we don't, the pipe buffer
# fills and the server hangs.
self._stderr_task = asyncio.create_task(self._drain_stderr())
# Start the reader loop.
self._reader_task = asyncio.create_task(self._reader_loop())
async def _drain_stderr(self) -> None:
if self._proc is None or self._proc.stderr is None:
return
try:
while True:
line = await self._proc.stderr.readline()
if not line:
break
text = line.decode("utf-8", errors="replace").rstrip()View on GitHub (pinned to c896c09c42)
Solutions
- Install the missing server (e.g. npm i -g typescript-language-server typescript, or pip install pyright) and confirm `which <cmd[0]>` succeeds in the same environment.
- Use an absolute path to the server binary in the LSP server configuration to remove PATH ambiguity.
- If a custom env is passed, ensure it includes the directory containing the binary (typically inherit os.environ and append).
Example fix
# before
client = LSPClient(cmd=["pyright-langserver", "--stdio"], cwd=project)
await client.start() # LSPProtocolError: binary not found
# after
import shutil
server = shutil.which("pyright-langserver") or "/opt/venvs/tools/bin/pyright-langserver"
client = LSPClient(cmd=[server, "--stdio"], cwd=project)
await client.start() Defensive patterns
Strategy: validation
Validate before calling
import shutil
def server_binary_available(cmd: list[str]) -> bool:
exe = cmd[0]
if "/" in exe or "\\" in exe:
import os
return os.path.isfile(exe) and os.access(exe, os.X_OK)
return shutil.which(exe) is not None Try / catch
try:
await client.start()
except LSPProtocolError as e:
if "binary not found" in str(e):
install_server_and_retry() # npm/pip install, then recreate the client
else:
raise Prevention
- shutil.which() the server binary at configuration time and fail with a helpful install hint.
- Prefer absolute paths for server binaries in config.
- If passing a custom env, include the directory containing the binary.
When it happens
Trigger: Starting an LSP session with cmd[0] like 'pyright-langserver', 'typescript-language-server', or 'gopls' when that binary is not installed or not on the subprocess's PATH (note env is passed explicitly, so a custom env without the tool's location also triggers it).
Common situations: Node-based servers installed locally via npm but npx/node_modules/.bin not on PATH; Python tools installed in a different virtualenv than the one running the agent; server name typo in configuration; Windows where the extension (.cmd/.exe) resolution differs.
Related errors
- cannot send {method!r}: stdin closed
- send failed for {method!r}: {e}
- unexpected EOF while reading LSP headers (partial={e.partial
- LSP header block exceeded 8 KiB without terminator
- truncated LSP body: expected {n} bytes, got {len(e.partial)}
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/806fe02fff356bbc.
Report an issue: GitHub.