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 eView on GitHub (pinned to c896c09c42)
Solutions
- 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.
- If it is a third-party server, file/upgrade — a server that omits Content-Length cannot talk LSP at all.
- 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
- If you implement the server, always write Content-Length (byte length) headers — never print raw JSON.
- Use existing framing helpers instead of hand-rolled writes.
- Smoke-test any custom server against a reference LSP client before integration.
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
- non-integer Content-Length: {cl!r}
- unreasonable Content-Length: {n}
- unexpected EOF while reading LSP headers (partial={e.partial
- LSP header block exceeded 8 KiB without terminator
- non-ASCII LSP header: {line!r}
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/f4cc34cc4aa04611.
Report an issue: GitHub.