tursodatabase/turso · error · anyhow::Error
Empty request
Error message
Empty request
What it means
After the \r\n\r\n terminator is found, parse_http_request takes the first line of the header block as the request line. This error fires when the header block is empty - i.e. the request bytes begin immediately with \r\n\r\n, so headers.lines().next() yields None. It is distinct from 778: framing was found, but there is no request line in front of it.
Solutions
- Start the request bytes directly with the request line: 'POST /v2/pipeline HTTP/1.1\r\n...'
- Strip stray leading CRLFs client-side before writing to the socket
- When embedding, map this to a 400 Bad Request response
Example fix
// before: stray leading blank lines stream.write_all(b"\r\n\r\nPOST /v2/pipeline HTTP/1.1\r\n\r\n")?; // after: request line first, single terminator stream.write_all(b"POST /v2/pipeline HTTP/1.1\r\nContent-Length: 0\r\n\r\n")?;
Defensive patterns
Strategy: validation
Validate before calling
// Client-side: request must start with the request line, not a blank line.
fn request_starts_with_request_line(raw: &[u8]) -> bool {
!raw.starts_with(b"\r\n") && !raw.starts_with(b"\n")
} Try / catch
let (method, path, body) = match parse_http_request(&request_data) {
Ok(parsed) => parsed,
Err(err) if err.to_string() == "Empty request" => {
respond_400(&mut stream)?; // leading blank lines: malformed request
return Ok(());
}
Err(err) => return Err(err),
}; Prevention
- Write the request line as the very first bytes on the socket
- In test scripts, strip stray newlines from templated request strings
- Treat empty-first-line requests as client bugs: fix the sender, not the server
When it happens
Trigger: A client sends leading blank lines before the request (bytes start with \r\n\r\n followed by the real request), or sends only a terminator with nothing before it; find_header_end returns 0 and the header slice is empty.
Common situations: Telnet sessions pressing Enter before typing the request; sloppy pipelining that leaves trailing blank lines from the previous response cycle; scripts concatenating request fragments incorrectly.
Related errors
- Invalid HTTP request
- Invalid request line
- HTTP request length overflows
- buffer too short for u32 at offset
- buffer too short for u64 at offset
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20).
Data as JSON: /api/errors/fb8b44a6a51291ee.
Report an issue: GitHub.
Appendix: source
Thrown at cli/sync_server.rs:1290
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 text_response(status: u16, body: &str) -> HttpResponse {
HttpResponse {
status,
content_type: "text/plain".to_string(),
body: body.as_bytes().to_vec(),View on GitHub (pinned to 492c4a71cd)