{"record":{"id":"9342a6931dd117fe","repo":"quickwit-oss/quickwit","slug":"body-stream-ended-with-bytes-pending-expected","errorCode":null,"errorMessage":"body stream ended with {} bytes pending; expected {target}","messagePattern":"body stream ended with (.+?) bytes pending; expected (.+?)","errorType":"exception","errorClass":"io::Error (UnexpectedEof)","httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-parquet-engine/src/storage/streaming_reader.rs","lineNumber":559,"sourceCode":"fn try_parse_page_header(buf: &[u8]) -> Result<(PageHeader, usize), thrift::Error> {\n    use parquet::thrift::TSerializable;\n    let mut cursor = io::Cursor::new(buf);\n    let mut prot = TCompactInputProtocol::new(&mut cursor);\n    let header = PageHeader::read_from_in_protocol(&mut prot)?;\n    Ok((header, cursor.position() as usize))\n}\n\n/// Ensure `state.pending` has at least `target` bytes, reading from\n/// the body stream as needed. Errors on premature EOF.\nasync fn fill_pending(state: &mut ReadingState, target: usize) -> io::Result<()> {\n    while state.pending.len() < target {\n        let buf_len = state.pending.len();\n        let to_alloc = (target - buf_len).max(8 * 1024);\n        state.pending.resize(buf_len + to_alloc, 0);\n        let n = state.body.read(&mut state.pending[buf_len..]).await?;\n        state.pending.truncate(buf_len + n);\n        if n == 0 {\n            return Err(io::Error::new(\n                io::ErrorKind::UnexpectedEof,\n                format!(\n                    \"body stream ended with {} bytes pending; expected {target}\",\n                    state.pending.len(),\n                ),\n            ));\n        }\n    }\n    Ok(())\n}\n\n/// Fill `state.pending` toward `target` bytes, but tolerate EOF\n/// (return `Ok(())` even if we can't reach the target). Used by the\n/// page-header parser, which iterates and decides whether the buffer\n/// is sufficient.\nasync fn fill_pending_best_effort(state: &mut ReadingState, target: usize) -> io::Result<()> {\n    while state.pending.len() < target {\n        let buf_len = state.pending.len();","sourceCodeStart":541,"sourceCodeEnd":577,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-parquet-engine/src/storage/streaming_reader.rs#L541-L577","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"fn is_truncated_body(err: &std::io::Error) -> bool { err.kind() == std::io::ErrorKind::UnexpectedEof }","tryCatchPattern":"match res {\n    Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {\n        warn!(\"truncated body, retrying with fresh range request\");\n        retry_with_backoff(|| reopen_and_read())\n    }\n    other => other,\n}","preventionTips":["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."],"tags":["io","network","parquet","unexpected-eof","streaming"],"backgroundTag":"http-request-failed","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}