{"record":{"id":"bc822843f6d6ef5c","repo":"NousResearch/hermes-agent","slug":"send-failed-for-method-r-e","errorCode":null,"errorMessage":"send failed for {method!r}: {e}","messagePattern":"send failed for (.+?): (.+?)","errorType":"exception","errorClass":"LSPProtocolError","httpStatus":null,"severity":"error","filePath":"agent/lsp/client.py","lineNumber":524,"sourceCode":"\n    # ------------------------------------------------------------------\n    # request / notification plumbing\n    # ------------------------------------------------------------------\n\n    async def _send_request(self, method: str, params: Any) -> Any:\n        if self._proc is None or self._proc.stdin is None or self._proc.stdin.is_closing():\n            raise LSPProtocolError(f\"cannot send {method!r}: stdin closed\")\n        loop = asyncio.get_running_loop()\n        req_id = self._next_id\n        self._next_id += 1\n        fut: asyncio.Future = loop.create_future()\n        self._pending[req_id] = fut\n        try:\n            self._proc.stdin.write(encode_message(make_request(req_id, method, params)))\n            await self._proc.stdin.drain()\n        except (BrokenPipeError, ConnectionResetError, OSError) as e:\n            self._pending.pop(req_id, None)\n            raise LSPProtocolError(f\"send failed for {method!r}: {e}\") from e\n        try:\n            return await fut\n        finally:\n            self._pending.pop(req_id, None)\n\n    async def _send_request_with_retry(self, method: str, params: Any, *, timeout: float) -> Any:\n        \"\"\"Send a request, retrying on ``ContentModified`` (-32801).\n\n        Other errors propagate.  The retry policy matches Claude Code's\n        ``LSPServerInstance.sendRequest`` — 3 attempts with delays\n        0.5s, 1.0s, 2.0s.\n        \"\"\"\n        for attempt in range(MAX_CONTENT_MODIFIED_RETRIES + 1):\n            try:\n                return await asyncio.wait_for(self._send_request(method, params), timeout=timeout)\n            except LSPRequestError as e:\n                if e.code == ERROR_CONTENT_MODIFIED and attempt < MAX_CONTENT_MODIFIED_RETRIES:\n                    await asyncio.sleep(RETRY_BASE_DELAY * (2 ** attempt))","sourceCodeStart":506,"sourceCodeEnd":542,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/lsp/client.py#L506-L542","documentation":"LSPProtocolError raised by _send_request when writing the serialized JSON-RPC message to the server's stdin fails with BrokenPipeError, ConnectionResetError, or a generic OSError. Unlike the 'stdin closed' pre-check, this means the write was attempted and the OS reported the pipe broken — the server died between the check and the write, or stdin was closed from the child side.","triggerScenarios":"Server process exits (crash, crash on malformed params, segfault in a native server like clangd) while a request is in flight; the pending future is popped and abandoned, so callers waiting on the result see this error instead of a hang.","commonSituations":"Native LSP servers (clangd, rust-analyzer) crashing on pathological source files; servers exiting after unrecoverable errors; rapid shutdown races in tests.","solutions":["Wrap request calls in a retry-with-restart: catch LSPProtocolError, recreate and start the client, re-open documents (didOpen), then retry the request once.","Check the server's exit code / stderr (drained at debug level) to find the crash cause — a crash loop means a server bug or bad input, not a client bug.","Pin a known-good server version if a recent release regressed stability."],"exampleFix":"# before\nresult = await client.request(\"textDocument/definition\", params)\n\n# after\nasync def request_with_restart(client, method, params):\n    try:\n        return await client.request(method, params)\n    except LSPProtocolError:\n        await client.stop()\n        await client.start()\n        await client.open_file(params[\"textDocument\"][\"uri\"].replace(\"file://\", \"\"))\n        return await client.request(method, params)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in range(2):\n    try:\n        return await client.request(method, params)\n    except LSPProtocolError as e:\n        if attempt == 0 and \"send failed\" in str(e):\n            await client.stop()\n            await client.start()\n            await client.open_file(current_path)\n            continue\n        raise","preventionTips":["Drain and log server stderr (the client already logs it at debug level) so crashes are diagnosable.","Design callers to tolerate one restart-and-replay: keep the set of open documents so state can be rebuilt.","Pin stable server versions; native servers crash-looping is a server bug."],"tags":["lsp","subprocess","retry"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}