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

  1. 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.
  2. Use an absolute path to the server binary in the LSP server configuration to remove PATH ambiguity.
  3. 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

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


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/806fe02fff356bbc. Report an issue: GitHub.