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

  1. Wrap request calls in a retry-with-restart: catch LSPProtocolError, recreate and start the client, re-open documents (didOpen), then retry the request once.
  2. 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.
  3. 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

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


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