{"id":"6162ddc841560d21","repo":"hyperium/hyper","slug":"invalid-chunk-body-lf","errorCode":null,"errorMessage":"Invalid chunk body LF","messagePattern":"Invalid chunk body LF","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"src/proto/h1/decode.rs","lineNumber":523,"sourceCode":"    fn read_body_cr<R: MemRead>(\n        cx: &mut Context<'_>,\n        rdr: &mut R,\n    ) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr, cx) {\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<R: MemRead>(\n        cx: &mut Context<'_>,\n        rdr: &mut R,\n    ) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr, cx) {\n            b'\\n' => Poll::Ready(Ok(ChunkedState::Start)),\n            _ => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid chunk body LF\",\n            ))),\n        }\n    }\n\n    fn read_trailer<R: MemRead>(\n        cx: &mut Context<'_>,\n        rdr: &mut R,\n        trailers_buf: &mut Option<BytesMut>,\n        h1_max_header_size: usize,\n    ) -> Poll<Result<ChunkedState, io::Error>> {\n        trace!(\"read_trailer\");\n        let byte = byte!(rdr, cx);\n\n        put_u8!(\n            trailers_buf.as_mut().expect(\"trailers_buf is None\"),\n            byte,","sourceCodeStart":505,"sourceCodeEnd":541,"githubUrl":"https://github.com/hyperium/hyper/blob/084473f728f9d07b3be5845475aa2f62ed9ff579/src/proto/h1/decode.rs#L505-L541","documentation":"Thrown in read_body_lf (src/proto/h1/decode.rs:523) after the CR following a chunk's data: the next byte must be '\\n' to complete the CRLF. Any other byte yields io::ErrorKind::InvalidInput — the chunk trailer CRLF is broken (CR present, LF missing).","triggerScenarios":"A chunk like \"10\\r\\n1234567890abcdef\\rX\" where the byte after the data-terminating CR is 'X' instead of LF; a transport or middleware that drops the LF after the data.","commonSituations":"A newline-normalizing proxy that converts CRLF→CR in the body; a buggy sender that writes only \"\\r\" after each chunk; byte-level corruption on the link.","solutions":["Ensure every chunk is followed by a full CRLF (\\r\\n), not a lone CR.","Disable any body newline normalization/transcoding in upstream proxies.","Write framing with a single literal \"\\r\\n\" buffer to avoid splitting CR and LF writes."],"exampleFix":"// before: CR and LF written through separate paths that drop LF\nwrite!(w, \"{:x}\\r\\n\", len).await?;\nw.write_all(&data).await?;\nw.write_all(b\"\\r\").await?; // LF lost downstream -> error 28\n\n// after: emit the full terminator atomically\nw.write_all(b\"\\r\\n\").await?;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"Some(Err(e)) => {\n    let kind = e.source()\n        .and_then(|s| s.downcast_ref::<std::io::Error>())\n        .map(|io| io.kind());\n    if matches!(kind, Some(std::io::ErrorKind::InvalidInput)) {\n        tracing::warn!(error=%e, \"chunk data terminator CR not followed by LF\");\n        break;\n    }\n    return Err(e.into());\n}","preventionTips":["Write each chunk's trailing CRLF as one literal b\"\\r\\n\" write.","Disable any proxy/middleware that normalizes CRLF→CR in the body.","Unit-test your chunker against hyper's Decoder::chunked(None,None) decoder."],"tags":["http","http1","chunked","framing","hyper","rust"],"analyzedSha":"084473f728f9d07b3be5845475aa2f62ed9ff579","analyzedAt":"2026-08-06T01:20:18.522Z","schemaVersion":2}