quickwit-oss/quickwit · error · io::Error (UnexpectedEof)
body stream ended with {} bytes pending; expected {target}
Error message
body stream ended with {} bytes pending; expected {target} What it means
`fill_pending` in the parquet streaming reader is topping up its internal pending buffer until it reaches `target` bytes. When the underlying body stream (HTTP object-storage body) returns 0 bytes (EOF) before the target is met, the reader raises UnexpectedEof, since the Parquet page header/data expected by the caller (`read_one_page`) is truncated.
Source
Thrown at quickwit/quickwit-parquet-engine/src/storage/streaming_reader.rs:559
fn try_parse_page_header(buf: &[u8]) -> Result<(PageHeader, usize), thrift::Error> {
use parquet::thrift::TSerializable;
let mut cursor = io::Cursor::new(buf);
let mut prot = TCompactInputProtocol::new(&mut cursor);
let header = PageHeader::read_from_in_protocol(&mut prot)?;
Ok((header, cursor.position() as usize))
}
/// Ensure `state.pending` has at least `target` bytes, reading from
/// the body stream as needed. Errors on premature EOF.
async fn fill_pending(state: &mut ReadingState, target: usize) -> io::Result<()> {
while state.pending.len() < target {
let buf_len = state.pending.len();
let to_alloc = (target - buf_len).max(8 * 1024);
state.pending.resize(buf_len + to_alloc, 0);
let n = state.body.read(&mut state.pending[buf_len..]).await?;
state.pending.truncate(buf_len + n);
if n == 0 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
format!(
"body stream ended with {} bytes pending; expected {target}",
state.pending.len(),
),
));
}
}
Ok(())
}
/// Fill `state.pending` toward `target` bytes, but tolerate EOF
/// (return `Ok(())` even if we can't reach the target). Used by the
/// page-header parser, which iterates and decides whether the buffer
/// is sufficient.
async fn fill_pending_best_effort(state: &mut ReadingState, target: usize) -> io::Result<()> {
while state.pending.len() < target {
let buf_len = state.pending.len();View on GitHub (pinned to a39730c5cd)
Solutions
- Retry the read/query — the reader's owner should re-open a fresh range request for the split bytes.
- Verify the split file integrity in object storage (size/checksum) and re-upload if truncated.
- Check for proxies/load balancers between Quickwit and the storage backend dropping long connections; raise their timeouts.
- If reproducible, enable storage request logging to capture the HTTP status that ended the body stream.
Defensive patterns
Strategy: retry
Type guard
fn is_truncated_body(err: &std::io::Error) -> bool { err.kind() == std::io::ErrorKind::UnexpectedEof } Try / catch
match res {
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
warn!("truncated body, retrying with fresh range request");
retry_with_backoff(|| reopen_and_read())
}
other => other,
} Prevention
- Enable HTTP client retries/idempotency for object-storage range reads.
- Monitor storage backend for truncated objects; verify sizes after upload.
- Raise load-balancer/proxy idle timeouts for long streaming reads.
When it happens
Trigger: Calling read_one_page on a StreamingPageReader whose remote body terminates early: connection reset mid-body, truncated upload in object storage, proxy timeout cutting the response, or a Content-Length/ETag mismatch.
Common situations: Unstable network links to S3/Azure/GCS; long-running queries whose HTTP connections are dropped by a load balancer; reading splits uploaded incompletely; storage gateway or CDN with aggressive idle timeouts.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- body stream ended after skipping {copied} bytes; expected to
- failed to get message from consumer: {:?}
- no server currently available
- failed to parse host: `{host}`
- failed to parse address `{}`: hostname is invalid
AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08).
Data as JSON: /api/errors/9342a6931dd117fe.
Report an issue: GitHub.