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

Invalid chunk body CR

Error message

Invalid chunk body CR

What it means

io::Error(InvalidInput, "Invalid chunk body CR") (chunked.rs:154-157) is returned by read_body_cr when, after consuming exactly <size> body bytes, the next byte is not a CR. RFC 7230 requires each chunk body be terminated by CRLF; any other byte here is a framing error.

Source

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

                slice = rdr.split().freeze();
                *rem -= len;
            } else {
                slice = rdr.split_to(*rem as usize).freeze();
                *rem = 0;
            }
            *buf = Some(slice);
            if *rem > 0 {
                Poll::Ready(Ok(ChunkedState::Body))
            } else {
                Poll::Ready(Ok(ChunkedState::BodyCr))
            }
        }
    }

    fn read_body_cr(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
        match byte!(rdr) {
            b'\r' => Poll::Ready(Ok(ChunkedState::BodyLf)),
            _ => Poll::Ready(Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "Invalid chunk body CR",
            ))),
        }
    }
    fn read_body_lf(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
        match byte!(rdr) {
            b'\n' => Poll::Ready(Ok(ChunkedState::Size)),
            _ => Poll::Ready(Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "Invalid chunk body LF",
            ))),
        }
    }
    fn read_end_cr(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
        match byte!(rdr) {
            b'\r' => Poll::Ready(Ok(ChunkedState::EndLf)),
            _ => Poll::Ready(Err(io::Error::new(

View on GitHub (pinned to 937960ca67)

Solutions

  1. Make the peer's declared chunk size exactly equal the number of body bytes that follow.
  2. Verify no intermediary is re-segmenting the body.
  3. Switch to Content-Length if the full size is known up front.
Defensive patterns

Strategy: try-catch

Try / catch

// Mismatched chunk length -> 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: The declared chunk size does not match the actual body length, so the parser reads the 'wrong' bytes as the terminator. e.g. declaring '4' but sending only 3 data bytes leaves the 4th data byte to be checked against CR.

Common situations: Encoder miscalculating chunk length, a proxy truncating or re-chunking the body, or a client streaming an incorrect size.

Related errors


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