{"record":{"id":"806fe02fff356bbc","repo":"NousResearch/hermes-agent","slug":"lsp-server-binary-not-found-cmd-0-e","errorCode":null,"errorMessage":"LSP server binary not found: {cmd[0]} ({e})","messagePattern":"LSP server binary not found: (.+?) \\((.+?)\\)","errorType":"exception","errorClass":"LSPProtocolError","httpStatus":null,"severity":"error","filePath":"agent/lsp/client.py","lineNumber":326,"sourceCode":"            # process group / session. Without this, the LSP server inherits\n            # the gateway's pgid (= TUI parent PID). When mcp_tool's\n            # _kill_orphaned_mcp_children races with LSP spawn and sweeps the\n            # gateway's child set, it captures the LSP PID, records the\n            # inherited pgid, and killpg() then kills the TUI parent itself.\n            # See tui_gateway_crash.log \"killpg → SIGTERM received\" stacks.\n            self._proc = await asyncio.create_subprocess_exec(\n                cmd[0],\n                *cmd[1:],\n                stdin=asyncio.subprocess.PIPE,\n                stdout=asyncio.subprocess.PIPE,\n                stderr=asyncio.subprocess.PIPE,\n                env=env,\n                cwd=self._cwd,\n                start_new_session=True,\n                creationflags=creationflags,\n            )\n        except FileNotFoundError as e:\n            raise LSPProtocolError(\n                f\"LSP server binary not found: {cmd[0]} ({e})\"\n            ) from e\n\n        # Drain stderr at debug level — if we don't, the pipe buffer\n        # fills and the server hangs.\n        self._stderr_task = asyncio.create_task(self._drain_stderr())\n        # Start the reader loop.\n        self._reader_task = asyncio.create_task(self._reader_loop())\n\n    async def _drain_stderr(self) -> None:\n        if self._proc is None or self._proc.stderr is None:\n            return\n        try:\n            while True:\n                line = await self._proc.stderr.readline()\n                if not line:\n                    break\n                text = line.decode(\"utf-8\", errors=\"replace\").rstrip()","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/lsp/client.py#L308-L344","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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)."],"exampleFix":"# before\nclient = LSPClient(cmd=[\"pyright-langserver\", \"--stdio\"], cwd=project)\nawait client.start()  # LSPProtocolError: binary not found\n\n# after\nimport shutil\nserver = shutil.which(\"pyright-langserver\") or \"/opt/venvs/tools/bin/pyright-langserver\"\nclient = LSPClient(cmd=[server, \"--stdio\"], cwd=project)\nawait client.start()","handlingStrategy":"validation","validationCode":"import shutil\n\ndef server_binary_available(cmd: list[str]) -> bool:\n    exe = cmd[0]\n    if \"/\" in exe or \"\\\\\" in exe:\n        import os\n        return os.path.isfile(exe) and os.access(exe, os.X_OK)\n    return shutil.which(exe) is not None","typeGuard":null,"tryCatchPattern":"try:\n    await client.start()\nexcept LSPProtocolError as e:\n    if \"binary not found\" in str(e):\n        install_server_and_retry()  # npm/pip install, then recreate the client\n    else:\n        raise","preventionTips":["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."],"tags":["lsp","subprocess","environment","configuration"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}