Hmbown/CodeWhale · error

LSP header exceeds size limit

Error message

LSP header exceeds size limit

What it means

parse_header scans the inbound byte buffer for the \r\n\r\n header terminator when no terminator has been found yet. If the accumulated buffer exceeds MAX_LSP_HEADER_BYTES without a terminator, the header can never be valid, so the client errors out. This exists so a broken or malicious LSP server cannot make the input buffer grow indefinitely.

Solutions

  1. Verify the configured LSP server binary actually implements the LSP stdio transport (Content-Length headers) rather than line-delimited JSON.
  2. Check that the server's stdout is clean — redirect debug logging to stderr, not stdout.
  3. Restart the server process; the stream is desynchronized and cannot recover mid-frame.
  4. If the server legitimately sends huge headers, raise MAX_LSP_HEADER_BYTES (with care) or upgrade the server.

Example fix

// before (wrapper script)
my-lsp-server --verbose   # logs to stdout
// after
my-lsp-server --verbose 2>server.log   # keep stdout protocol-only
Defensive patterns

Strategy: validation

Validate before calling

// sanity-check the server speaks LSP framing before wiring it up
let probe = std::process::Command::new(&server_bin).arg("--version").output()?;
if !probe.status.success() { return Err("binary is not a working LSP server"); }

Try / catch

match reader_result {
    Err(e) if e.to_string().contains("header exceeds size limit") => {
        // stream is desynchronized: kill and restart the server
        server.kill().await.ok();
        restart_with_framing_check().await
    }
    other => other,
}

Prevention

When it happens

Trigger: reader_task receives bytes from the server's stdout that never contain \r\n\r\n within MAX_LSP_HEADER_BYTES; e.g. the server emits binary garbage, raw JSON without an LSP framing header, or a corrupted stream.

Common situations: Pointing the client at a server that speaks plain JSON-RPC over stdio without Content-Length framing, a crashed server dumping a stack trace or binary output to stdout, or stdout/stderr wiring mixed up in a wrapper script.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/f42ef220f895f359. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/lsp/client.rs:589

            }
            let value = match serde_json::from_slice::<Value>(&buf[header_end..frame_end]) {
                Ok(value) => value,
                Err(_) => return,
            };
            buf.drain(..frame_end);
            if tx.send(value).await.is_err() {
                return;
            }
        }
    }
}

/// Distinguish incomplete headers from malformed or oversized frames so a
/// broken server cannot cause an indefinitely growing input buffer.
fn parse_header(buf: &[u8]) -> Result<Option<(usize, usize)>> {
    let Some(pos) = buf.windows(4).position(|window| window == b"\r\n\r\n") else {
        if buf.len() > MAX_LSP_HEADER_BYTES {
            return Err(anyhow!("LSP header exceeds size limit"));
        }
        return Ok(None);
    };
    if pos + 4 > MAX_LSP_HEADER_BYTES {
        return Err(anyhow!("LSP header exceeds size limit"));
    }
    let header = std::str::from_utf8(&buf[..pos]).context("invalid LSP header encoding")?;
    let mut content_length = None;
    for line in header.split("\r\n") {
        let (name, value) = line.split_once(':').context("malformed LSP header")?;
        if name.eq_ignore_ascii_case("Content-Length") {
            if content_length.is_some() {
                return Err(anyhow!("duplicate LSP Content-Length"));
            }
            let length = value
                .trim()
                .parse::<usize>()
                .context("invalid LSP Content-Length")?;

View on GitHub (pinned to 73e0f67d83)