Hmbown/CodeWhale · error

LSP frame exceeds size limit or is empty

Error message

LSP frame exceeds size limit or is empty

What it means

After parsing Content-Length, parse_header validates that the declared frame length is non-zero and does not exceed MAX_LSP_FRAME_BYTES. A zero-length body is not a valid LSP message and an oversized body could exhaust memory, so both are rejected before the client allocates the frame buffer.

Solutions

  1. Fix the server so every message has a non-empty JSON-RPC body within the size limit.
  2. Reduce oversized payloads server-side (e.g. huge completion results, massive diagnostics) or raise MAX_LSP_FRAME_BYTES deliberately.
  3. Treat this as a fatal stream error and restart the server connection.
  4. Audit the server for length-computation bugs (off-by-one, byte vs char counts).

Example fix

// before (server-side)
let frame = format!("Content-Length: {}\r\n\r\n", "");
// after
if body.is_empty() { return; } // skip empty notifications
let frame = format!("Content-Length: {}\r\n\r\n", body.len());
Defensive patterns

Strategy: validation

Validate before calling

// validate the declared length before allocating
fn sane_length(len: usize, max: usize) -> bool { len > 0 && len <= max }

Try / catch

match client.next_message().await {
    Err(e) if e.to_string().contains("size limit or is empty") => {
        // cannot trust declared sizes from this peer; restart
        restart_server().await?;
        Err(e)
    }
    other => other,
}

Prevention

When it happens

Trigger: Server advertises Content-Length: 0, or a Content-Length larger than MAX_LSP_FRAME_BYTES (e.g. a huge/ corrupted value, or an integer overflow/parse artifact like a bogus multi-gigabyte length).

Common situations: Buggy server sending keep-alive empty frames, a corrupted length field from binary junk in the stream, a malicious server attempting a memory-exhaustion DoS with an enormous declared length, or a truncation bug producing a zero-length final message.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


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

Appendix: source

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

        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")?;
            if length == 0 || length > MAX_LSP_FRAME_BYTES {
                return Err(anyhow!("LSP frame exceeds size limit or is empty"));
            }
            content_length = Some(length);
        }
    }
    Ok(Some((
        pos + 4,
        content_length.context("missing LSP Content-Length")?,
    )))
}

/// Background task that consumes inbound JSON values, classifies them as
/// notifications/responses, and routes accordingly.
async fn dispatcher_task(
    mut rx: mpsc::Receiver<Value>,
    tx_diag: mpsc::Sender<DiagnosticMessage>,
    pending: Arc<AsyncMutex<HashMap<i64, oneshot::Sender<Value>>>>,
) {
    while let Some(value) = rx.recv().await {

View on GitHub (pinned to 73e0f67d83)