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

  1. Force an English/UTF-8-safe environment for the server (LC_ALL=C.UTF-8) and ensure all diagnostics go to stderr.
  2. Update the server — Content-Length mismatches (the root cause of desync) are known bugs in several servers and get fixed.
  3. 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

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


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