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

Invalid chunk size line: missing size digit

Error message

Invalid chunk size line: missing size digit

What it means

Thrown by hyper's HTTP/1 chunked decoder in the Start state (src/proto/h1/decode.rs:364) when the very first byte of a chunk-size line is not a hexadecimal digit (0-9, a-f, A-F). The chunked transfer-encoding grammar requires each chunk to begin with a hex size token, so a non-hex leading byte means the peer sent malformed framing. It is reported as io::ErrorKind::InvalidInput and surfaces on a Body poll as a hyper::Error, aborting the body stream.

Source

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

    ) -> Poll<Result<ChunkedState, io::Error>> {
        trace!("Read chunk start");

        let radix = 16;
        match byte!(rdr, cx) {
            b @ b'0'..=b'9' => {
                *size = or_overflow!(size.checked_mul(radix));
                *size = or_overflow!(size.checked_add(u64::from(b - b'0')));
            }
            b @ b'a'..=b'f' => {
                *size = or_overflow!(size.checked_mul(radix));
                *size = or_overflow!(size.checked_add(u64::from(b + 10 - b'a')));
            }
            b @ b'A'..=b'F' => {
                *size = or_overflow!(size.checked_mul(radix));
                *size = or_overflow!(size.checked_add(u64::from(b + 10 - b'A')));
            }
            _ => {
                return Poll::Ready(Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "Invalid chunk size line: missing size digit",
                )));
            }
        }

        Poll::Ready(Ok(ChunkedState::Size))
    }

    fn read_size<R: MemRead>(
        cx: &mut Context<'_>,
        rdr: &mut R,
        size: &mut u64,
    ) -> Poll<Result<ChunkedState, io::Error>> {
        trace!("Read chunk hex size");

        let radix = 16;
        match byte!(rdr, cx) {

View on GitHub (pinned to 084473f728)

Solutions

  1. Capture the raw bytes off the wire and confirm each chunk begins with a hex size token (e.g. "1a\r\n<data>\r\n").
  2. If you own the sender, write the hex length token before each chunk's data, a trailing CRLF, and a final "0\r\n\r\n".
  3. Check any proxy/load-balancer between you and the peer for incorrect (re|de)-chunking.
  4. If the body is not meant to be streamed in chunks, send Content-Length instead of Transfer-Encoding: chunked.

Example fix

// before: writing raw data into a chunked response
write!(w, "{{\"ok\":true}}").await?; // peer sees no hex size -> error 20

// after: emit proper chunk framing
let payload = b"{\"ok\":true}";
write!(w, "{:x}\r\n", payload.len()).await?;
w.write_all(payload).await?;
write!(w, "\r\n0\r\n\r\n").await?;
Defensive patterns

Strategy: try-catch

Try / catch

// hyper surfaces decoder errors as hyper::Error when polling the body.
use hyper::body::Body;
let mut body = resp.into_body();
while let Some(chunk) = body.data().await {
    match chunk {
        Ok(bytes) => { /* accumulate */ }
        Err(e) => {
            let kind = e.source()
                .and_then(|s| s.downcast_ref::<std::io::Error>())
                .map(|io| io.kind());
            if matches!(kind, Some(std::io::ErrorKind::InvalidInput)) {
                // peer sent malformed chunked framing (e.g. non-hex leading byte)
                tracing::warn!(error=%e, "malformed chunk size line; dropping stream");
                break;
            }
            return Err(e.into());
        }
    }
}

Prevention

When it happens

Trigger: A chunked body whose chunk-size line begins with a non-hex byte: a bare "\r\n\r\n" at the start, a stray '\n', or a body that was never chunk-encoded but whose headers declared Transfer-Encoding: chunked (e.g. plain JSON streamed straight into a chunked response with no hex prefix).

Common situations: Hand-rolling a chunked response without writing the hex size prefix; an upstream proxy that strips or rewrites chunk framing; a buggy peer that emits an empty chunk line; mismatch between declared Transfer-Encoding and the actual bytes after a middleware or version change.

Related errors


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