ultraworkers/claw-code · error · std::io::Error
missing Content-Length header
Error message
missing Content-Length header
What it means
Server-side `read_frame` (runtime/src/mcp_server.rs:281): the header block terminated with a blank line (`\r\n` or `\n` both accepted here) but no header named Content-Length (matched case-insensitively after trimming) was parsed. Header lines without a `:` separator are silently ignored, so a misspelled or absent Content-Length lands here. `ErrorKind::InvalidData`.
Source
Thrown at rust/crates/runtime/src/mcp_server.rs:281
}
first_header = false;
if line == "\r\n" || line == "\n" {
break;
}
let header = line.trim_end_matches(['\r', '\n']);
if let Some((name, value)) = header.split_once(':') {
if name.trim().eq_ignore_ascii_case("Content-Length") {
let parsed = value
.trim()
.parse::<usize>()
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
content_length = Some(parsed);
}
}
}
let content_length = content_length.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "missing Content-Length header")
})?;
let mut payload = vec![0_u8; content_length];
reader.read_exact(&mut payload).await?;
Ok(Some(payload))
}
async fn write_response(
stdout: &mut Stdout,
response: &JsonRpcResponse<JsonValue>,
) -> io::Result<()> {
let body = serde_json::to_vec(response)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
let header = format!("Content-Length: {}\r\n\r\n", body.len());
stdout.write_all(header.as_bytes()).await?;
stdout.write_all(&body).await?;
stdout.flush().await
}
View on GitHub (pinned to 08106b0c37)
Solutions
- Fix the client to emit LSP-style framing: `Content-Length: <byte-len>\r\n\r\n<body>` with the exact byte length of the UTF-8 body.
- Test the client against a reference MCP peer to confirm framing before pointing it at claw.
- If you control a proxy in between, make sure it forwards the Content-Length header unmodified.
Example fix
# before (client writes headers without length)
printf 'Content-Type: application/json\r\n\r\n{"jsonrpc":"2.0"}' | claw mcp-serve
# after
BODY='{"jsonrpc":"2.0","id":1,"method":"initialize"}'
printf 'Content-Length: %d\r\n\r\n%s' "${#BODY}" "$BODY" | claw mcp-serve Defensive patterns
Strategy: try-catch
Try / catch
match read_frame(&mut reader).await {
Err(e) if e.kind() == io::ErrorKind::InvalidData
&& e.to_string().contains("missing Content-Length") => {
// protocol violation by the peer: close the connection, log the offending client
}
other => other,
} Prevention
- Emit strict `Content-Length: <len>\r\n\r\n<body>` framing, length = UTF-8 byte count
- Header name match is case-insensitive but must be exactly 'Content-Length' — no underscores
- Validate your client against a reference MCP server before pointing it at claw
When it happens
Trigger: A client sending only `Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n` with no length; `Content_length` misspelling (split at ':' works but the name match fails, so it is ignored); a hand-rolled client that sends the JSON body inline where headers should be and a stray blank line follows.
Common situations: Writing a custom MCP client and assuming newline-delimited JSON or that Content-Length is optional; adapting an HTTP client that sends Content-Type first and forgets the length; proxy middleware stripping the header.
Related errors
- missing Content-Length header
- MCP stdio stream closed while reading headers
- MCP stdio stream closed while reading headers
- MCP stdio stream closed while reading line
- MCP response for {method} used unsupported jsonrpc version `
AI-assisted analysis of ultraworkers/claw-code@08106b0c37 (2026-08-18).
Data as JSON: /api/errors/dea244f5ae5b6862.
Report an issue: GitHub.