{"record":{"id":"c4c5c669c356500c","repo":"NousResearch/hermes-agent","slug":"truncated-lsp-body-expected-n-bytes-got-len-e","errorCode":null,"errorMessage":"truncated LSP body: expected {n} bytes, got {len(e.partial)}","messagePattern":"truncated LSP body: expected (.+?) bytes, got (.+?)","errorType":"exception","errorClass":"LSPProtocolError","httpStatus":null,"severity":"error","filePath":"agent/lsp/protocol.py","lineNumber":120,"sourceCode":"            raise LSPProtocolError(f\"non-ASCII LSP header: {line!r}\") from e\n        if not key:\n            raise LSPProtocolError(f\"malformed LSP header line: {line!r}\")\n        headers[key.strip().lower()] = value.strip()\n\n    cl = headers.get(\"content-length\")\n    if cl is None:\n        raise LSPProtocolError(f\"LSP message missing Content-Length: {headers!r}\")\n    try:\n        n = int(cl)\n    except ValueError as e:\n        raise LSPProtocolError(f\"non-integer Content-Length: {cl!r}\") from e\n    if n < 0 or n > 64 * 1024 * 1024:  # 64 MiB sanity cap\n        raise LSPProtocolError(f\"unreasonable Content-Length: {n}\")\n\n    try:\n        body = await reader.readexactly(n)\n    except asyncio.IncompleteReadError as e:\n        raise LSPProtocolError(\n            f\"truncated LSP body: expected {n} bytes, got {len(e.partial)}\"\n        ) from e\n\n    try:\n        return json.loads(body.decode(\"utf-8\"))\n    except json.JSONDecodeError as e:\n        raise LSPProtocolError(f\"invalid JSON in LSP body: {e}\") from e\n    except UnicodeDecodeError as e:\n        raise LSPProtocolError(f\"non-UTF-8 LSP body: {e}\") from e\n\n\ndef make_request(req_id: int, method: str, params: Any) -> dict:\n    \"\"\"Build a JSON-RPC 2.0 request envelope.\"\"\"\n    msg: dict = {\"jsonrpc\": \"2.0\", \"id\": req_id, \"method\": method}\n    if params is not None:\n        msg[\"params\"] = params\n    return msg\n","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/lsp/protocol.py#L102-L138","documentation":"LSPProtocolError raised when readexactly(n) — reading the Content-Length-declared body — hits EOF after only len(e.partial) bytes. The server promised n bytes but died or closed stdout before delivering them, so the JSON body is incomplete and cannot be parsed.","triggerScenarios":"Server killed (OOM, signal) mid-write of a large response; server crashing while serializing a huge diagnostics payload; process exiting abruptly after sending headers (e.g. unhandled exception after write of headers but before body flush).","commonSituations":"Memory limits on containers running clangd/rust-analyzer with big workspaces; servers crashing on specific pathological files; forced shutdowns during heavy indexing.","solutions":["Restart the client and server, then re-open documents to retrigger the work — a mid-body death is a server crash, and stderr (drained at debug level) usually names the exception.","Raise memory/ulimits for the server process if it dies on large payloads.","If one specific file reliably kills the server, exclude it or update the server — report the crash upstream."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    msg = await read_message(reader)\nexcept LSPProtocolError as e:\n    if \"truncated LSP body\" in str(e):\n        client = await restart_client_and_reopen_docs(client)  # server died mid-write\n        msg = await read_message(reader)\n    else:\n        raise","preventionTips":["Raise memory limits for native servers (clangd, rust-analyzer) on large workspaces — OOM mid-response is the usual cause.","Watch server exit codes; a truncated body is always a dead server, and stderr holds why.","Build restart-and-replay into long-running LSP sessions so one crash does not kill the editor integration."],"tags":["lsp","protocol","eof","subprocess"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}