hyperium/hyper · error · std::io::Error

end of file before message length reached

Error message

end of file before message length reached

What it means

Thrown in the HTTP/1 body decoder (src/proto/h1/decode.rs:161) as io::Error::new(UnexpectedEof, IncompleteBody) where IncompleteBody Display is 'end of file before message length reached' (decode.rs:681-687). It fires in the Length decoder when a read returns 0 bytes (EOF) before the full Content-Length body has been received (decode.rs:160-164). It is the specific, body-level form of 'incomplete message': the peer promised N bytes but closed early.

Source

Thrown at src/proto/h1/decode.rs:161

    pub(crate) fn decode<R: MemRead>(
        &mut self,
        cx: &mut Context<'_>,
        body: &mut R,
    ) -> Poll<Result<Frame<Bytes>, io::Error>> {
        trace!("decode; state={:?}", self.kind);
        match self.kind {
            Length(ref mut remaining) => {
                if *remaining == 0 {
                    Poll::Ready(Ok(Frame::data(Bytes::new())))
                } else {
                    let to_read = usize::try_from(*remaining).unwrap_or(usize::MAX);
                    let buf = ready!(body.read_mem(cx, to_read))?;
                    let num = buf.as_ref().len() as u64;
                    if num > *remaining {
                        *remaining = 0;
                    } else if num == 0 {
                        return Poll::Ready(Err(io::Error::new(
                            io::ErrorKind::UnexpectedEof,
                            IncompleteBody,
                        )));
                    } else {
                        *remaining -= num;
                    }
                    Poll::Ready(Ok(Frame::data(buf)))
                }
            }
            Chunked {
                ref mut state,
                ref mut chunk_len,
                ref mut extensions_cnt,
                ref mut trailers_buf,
                ref mut trailers_cnt,
                ref h1_max_headers,
                ref h1_max_header_size,
            } => {

View on GitHub (pinned to 084473f728)

Solutions

  1. Confirm the peer actually sends exactly Content-Length bytes; fix length/transfer-encoding at the producer.
  2. On the receiving side, treat UnexpectedEof from the body as a corrupt/partial body — discard it and retry idempotent requests.
  3. If you proxy/transform bodies, recompute Content-Length (or use chunked) so you never advertise more than you send.

Example fix

// before: producer advertises a length it may not deliver
Response::builder()
    .header("content-length", body_len.to_string())
    .body(Body::from_stream(maybe_short_stream))?; // can EOF early

// after: use chunked when the length isn't guaranteed, or ensure the stream completes
Response::builder()
    // omit content-length; hyper will use chunked transfer-encoding
    .body(Body::from_stream(maybe_short_stream))?
Defensive patterns

Strategy: retry

Validate before calling

// Validate advertised length against your policy before trusting it.
fn acceptable_content_length(headers: &HeaderMap) -> bool {
    headers.get("content-length")
        .and_then(|v| v.to_str().ok())
        .and_then(|s| s.parse::<usize>().ok())
        .map(|n| n <= MAX_BODY_BYTES)
        .unwrap_or(true)
}

Type guard

fn is_truncated_body(err: &hyper::Error) -> bool {
    // The IncompleteBody io::Error surfaces via Kind::Body; check the source.
    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(bytes) => Ok(bytes),
    Err(e) if is_truncated_body(&e) && idempotent => { /* discard, retry whole request */ }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: A request/response has a Content-Length header but the connection EOFs after delivering fewer bytes; the Length decoder's read_mem returns an empty slice (num == 0) while remaining > 0 (decode.rs:156-164). Also produced in the chunked Body state when a chunk read returns 0 early (decode.rs:489-494).

Common situations: Upstream mis-reports Content-Length (too large) and closes after the real bytes; a proxy truncates the body; a client uploads then disconnects before finishing; TLS layer closes early; buffering middleware that miscalculates length.

Related errors


AI-assisted analysis of hyperium/hyper@084473f728 (2026-08-06). Data as JSON: /data/errors/111036985f028c04.json. Report an issue: GitHub.