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

Invalid chunk end CR

Error message

Invalid chunk end CR

What it means

io::Error(InvalidInput, "Invalid chunk end CR") (chunked.rs:172-175) is returned by read_end_cr after a zero-sized terminating chunk ('0' CRLF) when the next byte is not a CR. The chunked encoding trailer sequence is '0' CRLF *(trailer-field) CRLF; this state expects the final CR that closes the message.

Source

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

            _ => 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(
                io::ErrorKind::InvalidInput,
                "Invalid chunk end CR",
            ))),
        }
    }
    fn read_end_lf(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
        match byte!(rdr) {
            b'\n' => Poll::Ready(Ok(ChunkedState::End)),
            _ => Poll::Ready(Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "Invalid chunk end LF",
            ))),
        }
    }
}

#[cfg(test)]
mod tests {

View on GitHub (pinned to 937960ca67)

Solutions

  1. Ensure the chunked body ends with '0\r\n\r\n' (last-chunk plus empty trailer line).
  2. Check that the client is not closing the connection before flushing the trailer.
  3. Verify intermediaries pass the trailer through unchanged.

Example fix

// before: missing final terminator
b"4\r\ndata\r\n0\r\n"

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

Strategy: try-catch

Try / catch

// Missing final trailer CR -> bad request / incomplete body.
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: After the '0\r\n' last-chunk, the peer sends something other than CR to begin the final empty line, e.g. '0\r\nX'. read_size_lf transitioned to EndCr (size==0) and read_end_cr sees 'X'.

Common situations: A chunked body that omits the final CRLF terminator, or a proxy truncating the stream before the trailer.

Related errors


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