tursodatabase/turso · warning · anyhow::Error
Invalid request line
Error message
Invalid request line
What it means
The first line of the header block must split into at least two whitespace-separated tokens, which become parts[0] (method) and parts[1] (path). Fewer than two tokens means a line like 'GET' with no path, or binary garbage that happens to contain \r\n\r\n. The parser refuses to guess, because indexing parts[1] would panic.
Source
Thrown at cli/sync_server.rs:1179
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))
}
fn format_http_response(resp: &HttpResponse) -> Vec<u8> {
let status_text = match resp.status {
200 => "OK",
204 => "No Content",
404 => "Not Found",
500 => "Internal Server Error",
_ => "Unknown",
};
View on GitHub (pinned to bad083fafb)
Solutions
- Send a well-formed request line: 'METHOD /path HTTP/1.1' — at least method and path tokens.
- If a TLS client is involved, point it at the TLS port or fix the scheme mismatch.
- Log the offending first line to identify which client is misbehaving.
- For health checks, use 'GET /health HTTP/1.1' rather than bare words.
Example fix
// before (client)
write!(stream, "{}\r\n\r\n", command)?; // single-token request line
// after (client)
write!(stream, "GET {} HTTP/1.1\r\nHost: sync\r\n\r\n", path)?; Defensive patterns
Strategy: validation
Validate before calling
fn request_line_well_formed(data: &[u8]) -> bool {
let Some(header_end) = (0..data.len().saturating_sub(3))
.find(|&i| &data[i..i + 4] == b"\r\n\r\n")
else { return false; };
String::from_utf8_lossy(&data[..header_end])
.lines()
.next()
.is_some_and(|line| line.split_whitespace().count() >= 2)
}
// before parsing:
anyhow::ensure!(request_line_well_formed(&request_data), "request line needs method and path"); Type guard
fn request_line_well_formed(data: &[u8]) -> bool {
let Some(header_end) = (0..data.len().saturating_sub(3))
.find(|&i| &data[i..i + 4] == b"\r\n\r\n")
else { return false; };
String::from_utf8_lossy(&data[..header_end])
.lines()
.next()
.is_some_and(|line| line.split_whitespace().count() >= 2)
} Try / catch
match parse_http_request(&request_data) {
Ok((method, path, body)) => { /* dispatch */ }
Err(err) if err.to_string() == "Invalid request line" => {
// first line lacks method+path: reply 400 and close; log the line to find the client
}
Err(err) => return Err(err),
} Prevention
- Always send 'METHOD /path HTTP/1.1' as the first line.
- Check scheme and port when TLS clients are involved — TLS bytes to a plain port fail here.
- Log malformed request lines server-side to identify misbehaving clients.
- Use standard HTTP client libraries instead of hand-built request writers.
When it happens
Trigger: parse_http_request sees a request line that splits into 0 or 1 tokens: a client writing only 'GET\r\n', a bare 'PING', or TLS ClientHello bytes sent to the plain HTTP port where binary leading data precedes the terminator.
Common situations: HTTPS requests pointed at the plain HTTP port (TLS handshake bytes as the first line); custom minimal clients omitting the path or HTTP version; scripts writing shorthand request lines; protocol-confused probes.
Related errors
- Invalid HTTP request
- Empty request
- 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/6dc0a909ecd73b78.
Report an issue: GitHub.