NousResearch/hermes-agent · error · LSPProtocolError

LSP message missing Content-Length: {headers!r}

Error message

LSP message missing Content-Length: {headers!r}

What it means

LSPProtocolError raised when a complete header block was parsed but contained no Content-Length header. Content-Length is mandatory in the LSP base protocol (there is no chunked encoding), so the parser cannot know how many body bytes to read and refuses to guess.

Source

Thrown at agent/lsp/protocol.py:109

        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:
            raise LSPProtocolError(f"malformed LSP header line: {line!r}")
        headers[key.strip().lower()] = value.strip()

    cl = headers.get("content-length")
    if cl is None:
        raise LSPProtocolError(f"LSP message missing Content-Length: {headers!r}")
    try:
        n = int(cl)
    except ValueError as e:
        raise LSPProtocolError(f"non-integer Content-Length: {cl!r}") from e
    if n < 0 or n > 64 * 1024 * 1024:  # 64 MiB sanity cap
        raise LSPProtocolError(f"unreasonable Content-Length: {n}")

    try:
        body = await reader.readexactly(n)
    except asyncio.IncompleteReadError as e:
        raise LSPProtocolError(
            f"truncated LSP body: expected {n} bytes, got {len(e.partial)}"
        ) from e

    try:
        return json.loads(body.decode("utf-8"))
    except json.JSONDecodeError as e:
        raise LSPProtocolError(f"invalid JSON in LSP body: {e}") from e

View on GitHub (pinned to c896c09c42)

Solutions

  1. If it is your server, frame every outbound message: headers 'Content-Length: <utf8 byte length>\r\n\r\n' followed by the JSON body — length must be the byte count, not the string length.
  2. If it is a third-party server, file/upgrade — a server that omits Content-Length cannot talk LSP at all.
  3. Verify no other process is multiplexed onto the server's stdout.

Example fix

# before (server side)
print(json.dumps(message))  # client: missing Content-Length

# after (server side)
body = json.dumps(message).encode("utf-8")
sys.stdout.buffer.write(f"Content-Length: {len(body)}\r\n\r\n".encode() + body)
sys.stdout.buffer.flush()
Defensive patterns

Strategy: validation

Validate before calling

# server-side: frame every message before writing it
def frame_message(msg: dict) -> bytes:
    body = json.dumps(msg).encode("utf-8")
    return f"Content-Length: {len(body)}\r\n\r\n".encode("ascii") + body

Prevention

When it happens

Trigger: A server that sends only Content-Type before the blank line, a JSON-RPC message written without framing (raw json.dumps + newline — common in hand-rolled servers), or stdout noise that happens to parse as colon-lines then a blank line.

Common situations: Custom/in-house 'LSP-like' servers skipping the base protocol; servers reading LSP but writing responses via a plain print(json); partially-implemented test doubles.

Related errors


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