tursodatabase/turso · warning · anyhow::Error
Invalid HTTP request
Error message
Invalid HTTP request
What it means
parse_http_request first searches the accumulated bytes for the \r\n\r\n header terminator; none found yields this generic rejection. In the connection loop it typically surfaces when the peer closes the connection (read returns 0) before sending a complete header block, because oversized-but-terminator-less headers are caught earlier by the MAX_HEADER_BYTES check. It means: these bytes are not a complete HTTP request.
Source
Thrown at cli/sync_server.rs:1169
}
fn find_header_end(data: &[u8], start: usize) -> Option<usize> {
(start..data.len().saturating_sub(3)).find(|&i| &data[i..i + 4] == b"\r\n\r\n")
}
fn parse_content_length(headers: &str) -> Option<usize> {
for line in headers.lines() {
let lower = line.to_lowercase();
if lower.starts_with("content-length:") {
let value = line.split(':').nth(1)?.trim();
return value.parse().ok();
}
}
None
}
fn parse_http_request(data: &[u8]) -> Result<(String, String, Vec<u8>)> {
let header_end = find_header_end(data, 0).ok_or_else(|| anyhow!("Invalid HTTP request"))?;
let headers = String::from_utf8_lossy(&data[..header_end]);
let first_line = headers
.lines()
.next()
.ok_or_else(|| anyhow!("Empty request"))?;
let parts: Vec<&str> = first_line.split_whitespace().collect();
if parts.len() < 2 {
return Err(anyhow!("Invalid request line"));
}
let method = parts[0].to_string();
let path = parts[1].to_string();
let body = data[header_end + 4..].to_vec();
Ok((method, path, body))
}View on GitHub (pinned to bad083fafb)
Solutions
- From the client, send complete HTTP headers terminated by \r\n\r\n before the body.
- Use a real HTTP client (curl, reqwest) for health checks instead of raw TCP connects.
- If headers legitimately exceed the limit, raise MAX_HEADER_BYTES or split them.
- Retry the request in full; the server discards a request whose headers never completed.
Example fix
// before (client) stream.write_all(body)?; // raw bytes, no header block // after (client) stream.write_all(b"POST /sync HTTP/1.1\r\nHost: tursodb\r\nContent-Length: 4\r\n\r\n")?; stream.write_all(body)?;
Defensive patterns
Strategy: validation
Validate before calling
fn is_complete_http_request(data: &[u8]) -> bool {
find_header_end(data, 0).is_some()
}
// before parsing:
if !is_complete_http_request(&request_data) {
// peer closed before finishing headers: drop the connection
return Ok(());
}
let (method, path, body) = parse_http_request(&request_data)?; Type guard
fn is_complete_http_request(data: &[u8]) -> bool {
(0..data.len().saturating_sub(3)).any(|i| &data[i..i + 4] == b"\r\n\r\n")
} Try / catch
match parse_http_request(&request_data) {
Ok((method, path, body)) => { /* dispatch */ }
Err(err) if err.to_string() == "Invalid HTTP request" => {
// incomplete or non-HTTP bytes: respond 400 and close; do not retry reads
}
Err(err) => return Err(err),
} Prevention
- Send complete requests: headers terminated by \r\n\r\n, then the body.
- Use real HTTP clients for monitoring and health checks, not raw TCP connects.
- Handle peer close (read returns 0) as end-of-request, not as a parse attempt.
- Prefer a production HTTP server framework for anything beyond test tooling.
When it happens
Trigger: A peer sends bytes without \r\n\r\n and closes the connection, so handle_connection exits its read loop and parse_http_request fails; also direct calls to parse_http_request on non-HTTP bytes (raw TCP probes, TLS handshakes sent to the plain port).
Common situations: Health checks or monitoring probing the sync port with raw TCP instead of HTTP; clients timing out and closing mid-header; reverse proxies forwarding partial requests; test scripts sending hand-typed requests without the blank line.
Related errors
- Empty request
- Invalid request line
- HTTP status {e.code}: {message}
- HTTP request length overflows: {content_length}
- HTTP request failed: ${e instanceof Error ? e.message : Stri
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/57af175fc74ae5d4.
Report an issue: GitHub.