{"record":{"id":"aacbec76c53f3444","repo":"NousResearch/hermes-agent","slug":"cannot-send-method-r-stdin-closed","errorCode":null,"errorMessage":"cannot send {method!r}: stdin closed","messagePattern":"cannot send (.+?): stdin closed","errorType":"exception","errorClass":"LSPProtocolError","httpStatus":null,"severity":"error","filePath":"agent/lsp/client.py","lineNumber":513,"sourceCode":"                proc.terminate()\n                try:\n                    await asyncio.wait_for(proc.wait(), timeout=SHUTDOWN_GRACE)\n                except asyncio.TimeoutError:\n                    try:\n                        proc.kill()\n                        await proc.wait()\n                    except ProcessLookupError:\n                        pass\n            except ProcessLookupError:\n                pass\n\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).","sourceCodeStart":495,"sourceCodeEnd":531,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/lsp/client.py#L495-L531","documentation":"LSPProtocolError raised by _send_request when the server process is gone or its stdin pipe is closing — the request can never be delivered. It fires before any bytes are written, distinguishing it from the BrokenPipe 'send failed' variant: here the client already knows the transport is dead.","triggerScenarios":"Calling any request API (hover, completions, definitions) after the server crashed, after stop() was called, or after the reader loop detected EOF and tore down the process while pending futures were never awaited by the caller.","commonSituations":"Server process killed by OOM or by the user mid-session; reusing a client object across a restart; requests racing shutdown in tests.","solutions":["Check client.is_running before issuing requests and restart the client if it exited.","Register for the client's exit/error callback and fail fast in your own code when the server dies instead of continuing to send.","Investigate why the server terminated (stderr is drained at debug level — enable debug logs to see the crash reason)."],"exampleFix":"# before\nhover = await client.request(\"textDocument/hover\", params)  # may raise: stdin closed\n\n# after\nif not client.is_running:\n    await client.start()  # or recreate the client\nhover = await client.request(\"textDocument/hover\", params)","handlingStrategy":"try-catch","validationCode":"def can_send(client) -> bool:\n    return (\n        client.is_running\n        and client._proc is not None\n        and client._proc.stdin is not None\n        and not client._proc.stdin.is_closing()\n    )","typeGuard":null,"tryCatchPattern":"try:\n    result = await client.request(method, params)\nexcept LSPProtocolError as e:\n    if \"stdin closed\" in str(e) or \"send failed\" in str(e):\n        client = await restart_client(client)  # fresh process + re-open docs\n        result = await client.request(method, params)\n    else:\n        raise","preventionTips":["Check is_running before each request batch.","Subscribe to the client's exit notification to fail fast instead of sending into a dead pipe.","Never reuse a client object across stop()/start() cycles."],"tags":["lsp","lifecycle","subprocess"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}