actix/actix-web · error · io::Error
Invalid character in chunk extension
Error message
Invalid character in chunk extension
What it means
Raised in read_extension (chunked.rs:105) when a chunk extension contains a control character in the ranges 0x00-0x08, 0x0a-0x1f, or 0x7f. The extension parser only allows CR to terminate and tolerates printable bytes plus tab; bare LF or other controls are rejected (test hrs_chunk_extension_invalid at chunked.rs:385 sends "2;x\nx" to trigger it).
Solutions
- Keep chunk extensions free of raw control characters; quote values if they contain specials.
- Avoid relying on chunk extensions at all - most clients/servers ignore them.
- Treat repeated occurrences as hostile traffic and rate-limit/block the source.
Example fix
// before "2;x\nx\r\nxx\r\n" // after (no control chars in extension) "2;x=ok\r\nxx\r\n"
Defensive patterns
Strategy: try-catch
Try / catch
match payload.next().await {
Some(Err(PayloadError::Io(e))) if e.kind() == io::ErrorKind::InvalidInput =>
return HttpResponse::BadRequest().finish(), // control char in chunk ext
_ => { /* ... */ }
} Prevention
- Avoid chunk extensions entirely.
- Never put raw control bytes (LF/CR/NUL) in extensions.
- Block repeated offenders - this pattern is used in smuggling.
When it happens
Trigger: A chunk extension like "2;x\nx\r\n" contains a raw newline/control byte inside the extension value. The match at chunked.rs:105 returns the InvalidInput error.
Common situations: Request-smuggling attempts injecting LF into extensions to confuse parsers; a client that doesn't escape/quote extension values; malformed quoted-string handling.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid chunk size line: Size is too big
- Invalid chunk body CR
- Invalid chunk body LF
- Invalid chunk end CR
- Invalid chunk end LF
AI-assisted analysis of actix/actix-web@4d435abc28 (2026-08-09).
Data as JSON: /api/errors/53941aa976395a82.
Report an issue: GitHub.
Appendix: source
Thrown at actix-http/src/h1/chunked.rs:105
}
fn read_size_lws(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
match byte!(rdr) {
// 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(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
match byte!(rdr) {
b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
// strictly 0x20 (space) should be disallowed but we don't parse quoted strings here
0x00..=0x08 | 0x0a..=0x1f | 0x7f => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid character in chunk extension",
))),
_ => Poll::Ready(Ok(ChunkedState::Extension)), // no supported extensions
}
}
fn read_size_lf(rdr: &mut BytesMut, size: u64) -> Poll<Result<ChunkedState, io::Error>> {
match byte!(rdr) {
b'\n' if size > 0 => Poll::Ready(Ok(ChunkedState::Body)),
b'\n' if size == 0 => Poll::Ready(Ok(ChunkedState::EndCr)),
_ => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk size LF",
))),
}
}
fn read_body(View on GitHub (pinned to 4d435abc28)