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

Invalid chunk size linear white space

Error message

Invalid chunk size linear white space

What it means

Thrown in the SizeLws state (src/proto/h1/decode.rs:417) after linear whitespace (tab/space) following the chunk size. RFC 7230 allows LWS there, but the next byte must be more LWS, ';' (extension), or '\r' (end of line). Anything else yields io::ErrorKind::InvalidInput, e.g. a digit or letter appearing where only a terminator is permitted.

Source

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

                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(
                io::ErrorKind::InvalidInput,
                "Invalid chunk size linear white space",
            ))),
        }
    }
    fn read_extension<R: MemRead>(
        cx: &mut Context<'_>,
        rdr: &mut R,
        extensions_cnt: &mut u64,
    ) -> Poll<Result<ChunkedState, io::Error>> {
        trace!("read_extension");
        // We don't care about extensions really at all. Just ignore them.
        // They "end" at the next CRLF.
        //
        // However, some implementations may not check for the CR, so to save
        // them from themselves, we reject extensions containing plain LF as
        // well.
        match byte!(rdr, cx) {

View on GitHub (pinned to 084473f728)

Solutions

  1. Check that nothing follows the size's trailing whitespace except an optional ';ext' and a CRLF.
  2. Strip any annotation/comment the sender writes after the chunk size.
  3. Format sizes with no trailing decoration: "{:x}\r\n" or "{:x};ext\r\n".

Example fix

// before: annotation after padded size
write!(w, "{:x}   chunk#1\r\n", len).await?; // -> error 22

// after: optional extension only
write!(w, "{:x};name=val\r\n", len).await?;
Defensive patterns

Strategy: try-catch

Try / catch

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, "bad byte after chunk-size LWS");
        break; // drop stream, the framing is unrecoverable
    }
    return Err(e.into());
}

Prevention

When it happens

Trigger: A chunk-size line such as "Ff X\r\n" or "a q\r\n" — a non-LWS, non-terminator byte after the spaces following the hex size. Reading "1 A\r\n" also lands here once the space transitions to SizeLws.

Common situations: A sender that pads the size with spaces and then accidentally appends a comment or second size token; copy-paste of HTTP examples that include explanatory text after the size.

Related errors


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