NousResearch/hermes-agent · error · LSPProtocolError

unexpected EOF while reading LSP headers (partial={e.partial

Error message

unexpected EOF while reading LSP headers (partial={e.partial!r})

What it means

LSPProtocolError from the wire-framing reader: StreamReader.readuntil(b"\r\n") hit EOF partway through a header block. If nothing had been read and no headers accumulated, EOF is treated as a clean shutdown (returns None); a partial line or previously-parsed headers means the server died mid-message and the framing is corrupt.

Source

Thrown at agent/lsp/protocol.py:85

    """Read one Content-Length framed JSON-RPC message from the stream.

    Returns ``None`` on clean EOF (server closed stdout cleanly between
    messages — typical shutdown).  Raises :class:`LSPProtocolError` on
    malformed framing.

    The reader is advanced to just past the JSON body on success.
    """
    headers: dict = {}
    header_bytes = 0
    while True:
        try:
            line = await reader.readuntil(b"\r\n")
        except asyncio.IncompleteReadError as e:
            # EOF while reading headers.  If we hadn't started a header
            # block, treat as clean EOF; otherwise the framing is bad.
            if not e.partial and not headers:
                return None
            raise LSPProtocolError(
                f"unexpected EOF while reading LSP headers (partial={e.partial!r})"
            ) from e
        # Defensive cap against a server streaming headers without ever
        # emitting CRLF-CRLF.  Caps total header bytes at 8 KiB — a
        # well-behaved server fits in well under 200 bytes.
        header_bytes += len(line)
        if header_bytes > 8192:
            raise LSPProtocolError(
                "LSP header block exceeded 8 KiB without terminator"
            )
        line = line[:-2]  # strip CRLF
        if not line:
            break  # blank line ends header block
        try:
            key, _, value = line.decode("ascii").partition(":")
        except UnicodeDecodeError as e:
            raise LSPProtocolError(f"non-ASCII LSP header: {line!r}") from e
        if not key:

View on GitHub (pinned to c896c09c42)

Solutions

  1. Enable debug logging of the client's drained stderr to find the server's crash reason.
  2. Raise memory limits / disable heavy features for memory-hungry servers (clangd with large projects is the classic case).
  3. Ensure the server writes logs to stderr, never stdout — stdout is the LSP wire and any stray output corrupts framing.
Defensive patterns

Strategy: fallback

Try / catch

try:
    msg = await read_message(reader)
except LSPProtocolError as e:
    if "unexpected EOF" in str(e):
        mark_client_dead_and_fallback_to_no_lsp()  # degrade to non-LSP analysis
    else:
        raise

Prevention

When it happens

Trigger: Server process crashing or being killed exactly while emitting a response header block; server closing stdout without a final CRLF-terminated blank line after some headers; truncated output from a server killed by OOM.

Common situations: OOM-killed language servers; containers hitting memory limits; servers that log to stdout (breaking the protocol) and then exit.

Related errors


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