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

Invalid chunk size line: Invalid Size

Error message

Invalid chunk size line: Invalid Size

What it means

Thrown in the Size state (src/proto/h1/decode.rs:399) while consuming subsequent bytes of the chunk-size token. Only hex digits, linear whitespace (tab/space), ';' (start of an extension), or '\r' (end of the size line) are accepted; any other byte is reported as io::ErrorKind::InvalidInput. It means the size token contained an illegal character after a valid leading hex digit.

Source

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

        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')));
            }
            b'\t' | b' ' => return Poll::Ready(Ok(ChunkedState::SizeLws)),
            b';' => return Poll::Ready(Ok(ChunkedState::Extension)),
            b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)),
            _ => {
                return Poll::Ready(Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "Invalid chunk size line: Invalid Size",
                )));
            }
        }
        Poll::Ready(Ok(ChunkedState::Size))
    }
    fn read_size_lws<R: MemRead>(
        cx: &mut Context<'_>,
        rdr: &mut R,
    ) -> Poll<Result<ChunkedState, io::Error>> {
        trace!("read_size_lws");
        match byte!(rdr, cx) {
            // LWS can follow the chunk size, but no more digits can come
            b'\t' | b' ' => Poll::Ready(Ok(ChunkedState::SizeLws)),
            b';' => Poll::Ready(Ok(ChunkedState::Extension)),
            b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
            _ => Poll::Ready(Err(io::Error::new(

View on GitHub (pinned to 084473f728)

Solutions

  1. Inspect the exact bytes of the failing chunk-size line and locate the non-hex character.
  2. Ensure the sender emits only hex digits followed by optional LWS/extension and CRLF ("{:x}\r\n").
  3. Remove any middleware that decorates the size line with units, comments, or spaces before the CRLF.

Example fix

// before: decimal size with unit suffix
write!(w, "{} bytes\r\n", len).await?; // 'bytes' -> error 21

// after: lowercase hex, no suffix
write!(w, "{:x}\r\n", len).await?;
Defensive patterns

Strategy: try-catch

Try / catch

match body.data().await {
    Some(Ok(b)) => { /* process */ }
    Some(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)) {
            tracing::warn!(error=%e, "invalid chunk size token; aborting body");
            return Ok(None);
        }
        return Err(e.into());
    }
    None => {}
}

Prevention

When it happens

Trigger: A chunk-size line like "1X\r\n", "1 invalid extension\r\n", or "1 A\r\n" where a non-hex, non-terminator byte appears inside the size field. Also triggered by decimal sizes ("10\r\n" is fine because 1 and 0 are hex, but something like "1g\r\n" is not).

Common situations: A sender that writes the chunk length in decimal but appends a unit suffix ("16 bytes\r\n"); a debug logger or middleware that injects text into the size line; a peer that confuses chunk size with Content-Length formatting.

Related errors


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