NousResearch/hermes-agent · error · LSPProtocolError
non-ASCII LSP header: {line!r}
Error message
non-ASCII LSP header: {line!r} What it means
LSPProtocolError raised when a header line cannot be decoded as ASCII — the LSP base protocol requires ASCII headers (UTF-8 is only allowed in the JSON body). Non-ASCII bytes in the header region mean the stream is not LSP framing at all or the framing has desynced.
Source
Thrown at agent/lsp/protocol.py:102
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:
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(View on GitHub (pinned to c896c09c42)
Solutions
- Force an English/UTF-8-safe environment for the server (LC_ALL=C.UTF-8) and ensure all diagnostics go to stderr.
- Update the server — Content-Length mismatches (the root cause of desync) are known bugs in several servers and get fixed.
- If you control the server, double-check that byte length, not character length, is used for Content-Length.
Example fix
# before
env = {"PATH": ...} # inherits locale that makes server print localized output to stdout
# after
env = {**os.environ, "LC_ALL": "C.UTF-8"}
LSPClient(cmd=[server, "--stdio"], env=env, ...) Defensive patterns
Strategy: fallback
Try / catch
try:
msg = await read_message(reader)
except LSPProtocolError as e:
if "non-ASCII" in str(e) or "malformed" in str(e):
restart_with_clean_env_and_stderr_only_logging()
else:
raise Prevention
- Set LC_ALL=C.UTF-8 for server subprocesses so no localized output hits stdout.
- Keep server stdout reserved strictly for LSP framing; all logs to stderr.
- When framing desyncs once, restart the session — the stream rarely re-aligns.
When it happens
Trigger: The parser is reading where it expects headers but finds UTF-8 body or diagnostics text: desynced framing after a wrong Content-Length, a server printing localized/non-ASCII messages (e.g. a stack trace with accents or CJK) to stdout, or binary garbage after a crash.
Common situations: Server with a non-English locale printing errors to stdout; framing desync caused by an earlier message whose declared Content-Length did not match actual bytes written.
Related errors
- unexpected EOF while reading LSP headers (partial={e.partial
- LSP header block exceeded 8 KiB without terminator
- malformed LSP header line: {line!r}
- LSP message missing Content-Length: {headers!r}
- non-integer Content-Length: {cl!r}
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/b88e0eba12561acf.
Report an issue: GitHub.