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
- Check that nothing follows the size's trailing whitespace except an optional ';ext' and a CRLF.
- Strip any annotation/comment the sender writes after the chunk size.
- 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
- After the hex size, emit only an optional ';ext' and CRLF — nothing else.
- Avoid padding sizes with spaces in generated output; it's legal but fragile.
- Re-validate any proxy that rewrites chunk framing.
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
- Invalid chunk size line: missing size digit
- Invalid chunk size line: Invalid Size
- Invalid chunk size LF
- Invalid chunk body CR
- Invalid chunk body LF
AI-assisted analysis of hyperium/hyper@084473f728 (2026-08-06).
Data as JSON: /data/errors/2b5cfb3c1e41035d.json.
Report an issue: GitHub.