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

Invalid chunk body CR

Error message

Invalid chunk body CR

What it means

Raised in read_body_cr (chunked.rs:154) when the byte immediately following a chunk's body data is not CR. RFC 7230 requires every non-terminal chunk to be followed by CRLF; this state reads the CR after exactly 'size' body bytes and rejects anything else.

Solutions

  1. Ensure each chunk's declared hex size exactly equals the number of body bytes that follow before the CRLF.
  2. Use a vetted HTTP client library to produce chunked bodies.
  3. Validate/normalize chunked framing at any proxy boundary.

Example fix

// before
"5\r\nabcdX\r\n"  // size says 5 but body mismatch
// after
"4\r\nabcd\r\n"  // size matches body length
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(), // chunk body CR missing
    _ => { /* ... */ }
}

Prevention

When it happens

Trigger: The declared chunk size doesn't match the actual data length, so after consuming 'size' bytes the parser finds a non-CR byte. e.g. declaring "5\r\n" but only sending 4 data bytes before other content.

Common situations: Client mis-counting chunk payload length; truncated/injected bytes mid-stream; proxy re-chunking incorrectly; smuggling attempt with mismatched sizes.

Related errors


AI-assisted analysis of actix/actix-web@4d435abc28 (2026-08-09). Data as JSON: /api/errors/de359ad1015d0650. Report an issue: GitHub.

Appendix: 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 4d435abc28)