tursodatabase/turso · warning · anyhow::Error
Empty request
Error message
Empty request
What it means
The header block was found (data contains \r\n\r\n) but its first line is missing — in practice the data begins immediately with the terminator, i.e. leading blank lines before the request line, making lines().next() return None. RFC 7230 says servers SHOULD ignore at least one empty line before the request-line; this minimal parser does not, so it rejects. Keep-alive or telnet-style newline pings produce exactly this shape.
Source
Thrown at cli/sync_server.rs:1175
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))
}
fn format_http_response(resp: &HttpResponse) -> Vec<u8> {
let status_text = match resp.status {
200 => "OK",
204 => "No Content",
404 => "Not Found",View on GitHub (pinned to bad083fafb)
Solutions
- Client-side: send the request line first, with no leading blank lines.
- Server-side: skip leading empty lines before parsing (RFC 7230 recommends tolerating at least one).
- Check for keep-alive connection reuse bugs that leak a stray CRLF between requests.
Example fix
// before (server)
let first_line = headers.lines().next().ok_or_else(|| anyhow!("Empty request"))?;
// after (server): tolerate leading empty lines per RFC 7230
let first_line = headers
.lines()
.find(|line| !line.trim().is_empty())
.ok_or_else(|| anyhow!("Empty request"))?; Defensive patterns
Strategy: validation
Validate before calling
fn has_request_line(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; };
let headers = String::from_utf8_lossy(&data[..header_end]);
headers.lines().any(|line| !line.trim().is_empty())
}
// before parsing:
anyhow::ensure!(has_request_line(&request_data), "no request line before terminator"); Type guard
fn has_request_line(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()
.any(|line| !line.trim().is_empty())
} Try / catch
match parse_http_request(&request_data) {
Ok((method, path, body)) => { /* dispatch */ }
Err(err) if err.to_string() == "Empty request" => {
// only blank lines before the terminator: reply 400 or tolerate per RFC 7230
}
Err(err) => return Err(err),
} Prevention
- Clients: never flush leading CRLFs before the request line.
- Servers: skip at least one leading empty line before parsing, as RFC 7230 suggests.
- Audit keep-alive client code for stray newlines leaked between requests.
- For manual tests, type the request line immediately after connecting.
When it happens
Trigger: parse_http_request receives data starting with \r\n\r\n or whose header section before the terminator is only empty lines — a client that flushes a stray CRLF before the request line, or manual telnet testing that presses Enter first.
Common situations: Manual telnet/netcat testing with a leading blank line; HTTP clients that emit a leftover CRLF from a previous keep-alive exchange; load balancers or wrappers injecting empty lines; scripts writing '\r\nGET ...'.
Related errors
- Invalid HTTP 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/fb8b44a6a51291ee.
Report an issue: GitHub.