hyperium/hyper · error · std::io::Error
unexpected EOF during chunk size line
Error message
unexpected EOF during chunk size line
What it means
Thrown in the HTTP/1 chunked decoder via the byte! macro (src/proto/h1/decode.rs:258) as io::Error::new(UnexpectedEof, "unexpected EOF during chunk size line"). It fires when the connection EOFs while the parser is still reading the hex chunk-size line of a Transfer-Encoding: chunked body — i.e. the chunked stream was truncated before the next size token was complete.
Source
Thrown at src/proto/h1/decode.rs:258
#[cfg(test)]
async fn decode_fut<R: MemRead>(&mut self, body: &mut R) -> Result<Frame<Bytes>, io::Error> {
futures_util::future::poll_fn(move |cx| self.decode(cx, body)).await
}
}
impl fmt::Debug for Decoder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.kind, f)
}
}
macro_rules! byte (
($rdr:ident, $cx:expr) => ({
let buf = ready!($rdr.read_mem($cx, 1))?;
if !buf.is_empty() {
buf[0]
} else {
return Poll::Ready(Err(io::Error::new(io::ErrorKind::UnexpectedEof,
"unexpected EOF during chunk size line")));
}
})
);
macro_rules! or_overflow {
($e:expr) => (
match $e {
Some(val) => val,
None => return Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid chunk size: overflow",
))),
}
)
}
macro_rules! put_u8 {View on GitHub (pinned to 084473f728)
Solutions
- Confirm the peer sends the full chunked body including the terminating zero-length chunk and trailer CRLF.
- On the receiving side, treat this UnexpectedEof as a truncated body — discard partial data; retry idempotent requests on a new connection.
- If you generate chunked output, ensure the stream always emits the final 0-length chunk even on error paths.
Example fix
// before: producer can drop without finishing the chunked stream
let body = Body::wrap_stream(producer_that_may_panic());
// after: guarantee the terminal chunk by completing or converting to an error status
let body = Body::wrap_stream(async_stream::stream! {
let mut s = producer_that_may_panic();
while let Some(item) = s.next().await {
match item {
Ok(b) => yield Ok::<_, std::io::Error>(b),
Err(e) => { tracing::error!("producer failed: {e}"); break; }
}
}
}); Defensive patterns
Strategy: retry
Type guard
fn is_chunk_eof(err: &hyper::Error) -> bool {
matches!(
err.source().and_then(|s| s.downcast_ref::<std::io::Error>()).map(|io| io.kind()),
Some(std::io::ErrorKind::UnexpectedEof)
)
} Try / catch
match hyper::body::to_bytes(resp.into_body()).await {
Ok(b) => Ok(b),
Err(e) if is_chunk_eof(&e) && idempotent => { /* truncated chunked body: retry */ }
Err(e) => Err(e),
} Prevention
- Ensure chunked producers always emit the final zero-length chunk and trailer CRLF, even on error.
- Treat UnexpectedEof mid-chunk as a truncated body; discard and retry idempotent requests.
- Watch for proxies that time out and drop chunked streams before the terminating chunk.
When it happens
Trigger: The byte! macro (decode.rs:252-262) calls read_mem(1) and gets an empty buffer (EOF) while in Size/SizeLws/Extension/SizeLf/BodyCr/BodyLf/Trailer states — i.e. anywhere a chunk framing byte is expected. Happens when the peer closes the connection between chunks instead of sending the terminating 0-size chunk.
Common situations: A chunked response is cut off before the final '0\r\n\r\n'; a streaming producer crashes mid-chunk; a proxy times out and drops a chunked body; network reset between chunks. The decoder reached the end of stream before it saw the chunk-size digits it needed.
Related errors
- end of file before message length reached
- connection closed before message completed
- error reading a body from connection
- invalid chunk size: overflow
- chunk trailers bytes over limit
AI-assisted analysis of hyperium/hyper@084473f728 (2026-08-06).
Data as JSON: /data/errors/958d537ff3ff5e2f.json.
Report an issue: GitHub.