NousResearch/hermes-agent · error · LSPProtocolError
send failed for {method!r}: {e}
Error message
send failed for {method!r}: {e} What it means
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.
Source
Thrown at agent/lsp/client.py:524
# ------------------------------------------------------------------
# request / notification plumbing
# ------------------------------------------------------------------
async def _send_request(self, method: str, params: Any) -> Any:
if self._proc is None or self._proc.stdin is None or self._proc.stdin.is_closing():
raise LSPProtocolError(f"cannot send {method!r}: stdin closed")
loop = asyncio.get_running_loop()
req_id = self._next_id
self._next_id += 1
fut: asyncio.Future = loop.create_future()
self._pending[req_id] = fut
try:
self._proc.stdin.write(encode_message(make_request(req_id, method, params)))
await self._proc.stdin.drain()
except (BrokenPipeError, ConnectionResetError, OSError) as e:
self._pending.pop(req_id, None)
raise LSPProtocolError(f"send failed for {method!r}: {e}") from e
try:
return await fut
finally:
self._pending.pop(req_id, None)
async def _send_request_with_retry(self, method: str, params: Any, *, timeout: float) -> Any:
"""Send a request, retrying on ``ContentModified`` (-32801).
Other errors propagate. The retry policy matches Claude Code's
``LSPServerInstance.sendRequest`` — 3 attempts with delays
0.5s, 1.0s, 2.0s.
"""
for attempt in range(MAX_CONTENT_MODIFIED_RETRIES + 1):
try:
return await asyncio.wait_for(self._send_request(method, params), timeout=timeout)
except LSPRequestError as e:
if e.code == ERROR_CONTENT_MODIFIED and attempt < MAX_CONTENT_MODIFIED_RETRIES:
await asyncio.sleep(RETRY_BASE_DELAY * (2 ** attempt))View on GitHub (pinned to c896c09c42)
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.
Example fix
# before
result = await client.request("textDocument/definition", params)
# after
async def request_with_restart(client, method, params):
try:
return await client.request(method, params)
except LSPProtocolError:
await client.stop()
await client.start()
await client.open_file(params["textDocument"]["uri"].replace("file://", ""))
return await client.request(method, params) Defensive patterns
Strategy: retry
Try / catch
for attempt in range(2):
try:
return await client.request(method, params)
except LSPProtocolError as e:
if attempt == 0 and "send failed" in str(e):
await client.stop()
await client.start()
await client.open_file(current_path)
continue
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Native LSP servers (clangd, rust-analyzer) crashing on pathological source files; servers exiting after unrecoverable errors; rapid shutdown races in tests.
Related errors
- LSP server binary not found: {cmd[0]} ({e})
- cannot send {method!r}: stdin closed
- unexpected EOF while reading LSP headers (partial={e.partial
- truncated LSP body: expected {n} bytes, got {len(e.partial)}
- Could not refresh the remote gateway WebSocket ticket.
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/bc822843f6d6ef5c.
Report an issue: GitHub.