hyperium/hyper · error · std::io::Error
Invalid chunk end LF
Error message
Invalid chunk end LF
What it means
Thrown in read_end_lf (src/proto/h1/decode.rs:630) for the final terminator of the chunked stream: after the closing '\r' (the blank line that ends the trailer block / body), the next byte must be '\n'. Any other byte yields io::ErrorKind::InvalidInput — the chunked body's terminating CRLF is broken.
Source
Thrown at src/proto/h1/decode.rs:630
Poll::Ready(Ok(ChunkedState::Trailer))
}
}
}
fn read_end_lf<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
trailers_buf: &mut Option<BytesMut>,
h1_max_header_size: usize,
) -> Poll<Result<ChunkedState, io::Error>> {
let byte = byte!(rdr, cx);
match byte {
b'\n' => {
if let Some(trailers_buf) = trailers_buf {
put_u8!(trailers_buf, byte, h1_max_header_size);
}
Poll::Ready(Ok(ChunkedState::End))
}
_ => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk end LF",
))),
}
}
}
// TODO: disallow Transfer-Encoding, Content-Length, Trailer, etc in trailers ??
fn decode_trailers(buf: &mut BytesMut, count: usize) -> Result<HeaderMap, io::Error> {
let mut trailers = HeaderMap::new();
let mut headers = vec![httparse::EMPTY_HEADER; count];
let res = httparse::parse_headers(buf, &mut headers);
match res {
Ok(httparse::Status::Complete((_, headers))) => {
for header in headers {
use std::convert::TryFrom;
let name = match HeaderName::try_from(header.name) {
Ok(name) => name,View on GitHub (pinned to 084473f728)
Solutions
- Confirm the chunked stream ends with the full "0\r\n\r\n" (zero chunk + blank line).
- Make sure the sender flushes and only then closes the connection.
- Check for proxies/load-balancers known to truncate the final byte of a streamed response.
Example fix
// before: closing after the CR write!(w, "0\r\n\r").await?; // -> error 31 // after: full terminating sequence write!(w, "0\r\n\r\n").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, "chunked terminator CR not followed by LF");
break;
}
return Err(e.into());
} Prevention
- Always close a chunked stream with the full "0\r\n\r\n" (zero chunk + blank line).
- Flush and only then close the connection; don't half-close mid-terminator.
- Watch for middleboxes that truncate the final byte of a streamed response.
When it happens
Trigger: A chunked body that ends with "0\r\n\r" but never sends the final LF, or sends a different byte there — e.g. "0\r\n\rX". The EndLf state is reached from EndCr after the final CR.
Common situations: A sender that closes the socket immediately after the final CR without the LF; a transport bug that drops the very last byte; a proxy that truncates the response tail.
Related errors
- Invalid chunk size line: missing size digit
- Invalid chunk size line: Invalid Size
- Invalid chunk size linear white space
- Invalid chunk size LF
- Invalid chunk body CR
AI-assisted analysis of hyperium/hyper@084473f728 (2026-08-06).
Data as JSON: /data/errors/73a225c0a2a35452.json.
Report an issue: GitHub.