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

Invalid chunk size LF

Error message

Invalid chunk size LF

What it means

Thrown in read_size_lf (src/proto/h1/decode.rs:469) after the decoder saw '\r' ending the chunk-size line and expects the following '\n'. Any byte other than '\n' yields io::ErrorKind::InvalidInput, meaning the size line's CRLF terminator was malformed (a CR not followed by LF).

Source

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

            } // no supported extensions
        }
    }
    fn read_size_lf<R: MemRead>(
        cx: &mut Context<'_>,
        rdr: &mut R,
        size: u64,
    ) -> Poll<Result<ChunkedState, io::Error>> {
        trace!("Chunk size is {:?}", size);
        match byte!(rdr, cx) {
            b'\n' => {
                if size == 0 {
                    Poll::Ready(Ok(ChunkedState::EndCr))
                } else {
                    debug!("incoming chunked header: {0:#X} ({0} bytes)", size);
                    Poll::Ready(Ok(ChunkedState::Body))
                }
            }
            _ => Poll::Ready(Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "Invalid chunk size LF",
            ))),
        }
    }

    fn read_body<R: MemRead>(
        cx: &mut Context<'_>,
        rdr: &mut R,
        rem: &mut u64,
        buf: &mut Option<Bytes>,
    ) -> Poll<Result<ChunkedState, io::Error>> {
        trace!("Chunked read, remaining={:?}", rem);

        // cap remaining bytes at the max capacity of usize
        let to_read = usize::try_from(*rem).unwrap_or(usize::MAX);
        let slice = ready!(rdr.read_mem(cx, to_read))?;
        let count = slice.len();

View on GitHub (pinned to 084473f728)

Solutions

  1. Confirm the size line is terminated by a true CRLF (\r\n), not a lone CR.
  2. Audit any middleware that transforms newlines in the response body.
  3. Re-emit framing with write!(w, "{:x}\r\n", len) so both bytes are always written.

Example fix

// before: only CR written
write!(w, "{:x}\r", len).await?; // -> error 25

// after: full CRLF
write!(w, "{:x}\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, "chunk size line CR not followed by LF");
        break;
    }
    return Err(e.into());
}

Prevention

When it happens

Trigger: A chunk-size line ending in CR but not LF, e.g. "F\rF" (CR followed by 'F'), or a sender that writes "\r" alone and then data with no LF.

Common situations: A peer or middleware that strips/converts LF; a transport that drops the LF byte; copy-paste of chunk examples that used a bare CR.

Related errors


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