actix/actix-web · error · io::Error

Invalid chunk size LF

Error message

Invalid chunk size LF

What it means

io::Error(InvalidInput, "Invalid chunk size LF") (chunked.rs:116-119) is returned by read_size_lf when the byte following the chunk size (and optional extension) is not a lone LF after the expected CR. The state machine reaches SizeLf only after consuming a CR; a byte other than '\n' here breaks the required CRLF terminator.

Source

Thrown at actix-http/src/h1/chunked.rs:116

            ))),
        }
    }
    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(
        rdr: &mut BytesMut,
        rem: &mut u64,
        buf: &mut Option<Bytes>,
    ) -> Poll<Result<ChunkedState, io::Error>> {
        trace!("Chunked read, remaining={:?}", rem);

        let len = rdr.len() as u64;
        if len == 0 {
            Poll::Ready(Ok(ChunkedState::Body))
        } else {
            let slice;

View on GitHub (pinned to 937960ca67)

Solutions

  1. Ensure every size line ends with a strict CRLF (0x0D 0x0A).
  2. Use Content-Length when the body length is known.
  3. Capture the wire bytes to confirm the terminator is missing.

Example fix

// before
b"4\rX\ndata\r\n0\r\n\r\n"

// after
b"4\r\ndata\r\n0\r\n\r\n"
Defensive patterns

Strategy: try-catch

Try / catch

// Handle the malformed CRLF as a 400 bad request.
use actix_http::error::PayloadError;
if matches!(payload.next().await, Some(Err(PayloadError::Io(e))) if e.kind() == io::ErrorKind::InvalidInput) {
    return HttpResponse::BadRequest().finish();
}

Prevention

When it happens

Trigger: A chunk size line uses a bare CR without LF, or inserts a character between CR and LF, e.g. '4\rX\n'. read_size emits SizeLf on the CR and read_size_lf sees 'X'.

Common situations: A non-conformant encoder using lone CR or inserting bytes inside the line terminator; line-ending corruption by a proxy.

Related errors


AI-assisted analysis of actix/actix-web@937960ca67 (2026-08-06). Data as JSON: /data/errors/b4f995461c9eedce.json. Report an issue: GitHub.