{"record":{"id":"de359ad1015d0650","repo":"actix/actix-web","slug":"invalid-chunk-body-cr","errorCode":null,"errorMessage":"Invalid chunk body CR","messagePattern":"Invalid chunk body CR","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"actix-http/src/h1/chunked.rs","lineNumber":154,"sourceCode":"                slice = rdr.split().freeze();\n                *rem -= len;\n            } else {\n                slice = rdr.split_to(*rem as usize).freeze();\n                *rem = 0;\n            }\n            *buf = Some(slice);\n            if *rem > 0 {\n                Poll::Ready(Ok(ChunkedState::Body))\n            } else {\n                Poll::Ready(Ok(ChunkedState::BodyCr))\n            }\n        }\n    }\n\n    fn read_body_cr(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            b'\\r' => Poll::Ready(Ok(ChunkedState::BodyLf)),\n            _ => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid chunk body CR\",\n            ))),\n        }\n    }\n    fn read_body_lf(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            b'\\n' => Poll::Ready(Ok(ChunkedState::Size)),\n            _ => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid chunk body LF\",\n            ))),\n        }\n    }\n    fn read_end_cr(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            b'\\r' => Poll::Ready(Ok(ChunkedState::EndLf)),\n            _ => Poll::Ready(Err(io::Error::new(","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/actix/actix-web/blob/4d435abc281842f3cbee165b6cde739e001d3a25/actix-http/src/h1/chunked.rs#L136-L172","documentation":"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.","triggerScenarios":"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.","commonSituations":"Client mis-counting chunk payload length; truncated/injected bytes mid-stream; proxy re-chunking incorrectly; smuggling attempt with mismatched sizes.","solutions":["Ensure each chunk's declared hex size exactly equals the number of body bytes that follow before the CRLF.","Use a vetted HTTP client library to produce chunked bodies.","Validate/normalize chunked framing at any proxy boundary."],"exampleFix":"// before\n\"5\\r\\nabcdX\\r\\n\"  // size says 5 but body mismatch\n// after\n\"4\\r\\nabcd\\r\\n\"  // size matches body length","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match payload.next().await {\n    Some(Err(PayloadError::Io(e))) if e.kind() == io::ErrorKind::InvalidInput =>\n        return HttpResponse::BadRequest().finish(), // chunk body CR missing\n    _ => { /* ... */ }\n}","preventionTips":["Make declared chunk sizes exactly match the following body byte count.","Always append CRLF after each chunk body.","Use a mature HTTP serializer."],"tags":["http","chunked-encoding","actix-http","protocol"],"backgroundTag":null,"analyzedSha":"4d435abc281842f3cbee165b6cde739e001d3a25","analyzedAt":"2026-08-09T01:01:40.926Z","contentChangedAt":"2026-08-09T01:01:40.926Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}