hyperium/hyper · error · std::io::Error

Invalid trailer end LF

Error message

Invalid trailer end LF

What it means

Thrown in read_trailer_lf (src/proto/h1/decode.rs:578) for a trailer line that began with '\r' (transitioning to TrailerLf) but whose next byte is not '\n'. A trailer header must be terminated by CRLF; a CR not followed by LF yields io::ErrorKind::InvalidInput.

Source

Thrown at src/proto/h1/decode.rs:578

        match byte {
            b'\n' => {
                if *trailers_cnt >= h1_max_headers {
                    return Poll::Ready(Err(io::Error::new(
                        io::ErrorKind::InvalidData,
                        "chunk trailers count overflow",
                    )));
                }
                *trailers_cnt += 1;

                put_u8!(
                    trailers_buf.as_mut().expect("trailers_buf is None"),
                    byte,
                    h1_max_header_size
                );

                Poll::Ready(Ok(ChunkedState::EndCr))
            }
            _ => Poll::Ready(Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "Invalid trailer end LF",
            ))),
        }
    }

    fn read_end_cr<R: MemRead>(
        cx: &mut Context<'_>,
        rdr: &mut R,
        trailers_buf: &mut Option<BytesMut>,
        h1_max_header_size: usize,
    ) -> Poll<Result<ChunkedState, io::Error>> {
        let byte = byte!(rdr, cx);
        match byte {
            b'\r' => {
                if let Some(trailers_buf) = trailers_buf {
                    put_u8!(trailers_buf, byte, h1_max_header_size);
                }

View on GitHub (pinned to 084473f728)

Solutions

  1. Ensure every trailer line ends with a true CRLF (\r\n).
  2. Validate/normalize newline handling for trailer-encoding middleware.
  3. Write the trailer block from a single buffer so CR and LF can't be split.

Example fix

// before: trailer written with lone CR
write!(w, "0\r\nx-trace: abc\r").await?; // -> error 30
write!(w, "\r\n").await?;

// after: each trailer terminated by CRLF, then blank line
write!(w, "0\r\nx-trace: abc\r\n\r\n").await?;
Defensive patterns

Strategy: try-catch

Try / catch

Some(Err(e)) => {
    let kind = e.source()
        .and_then(|s| s.downcast_ref::<std::io::Error>())
        .map(|io| io.kind());
    if matches!(kind, Some(std::io::ErrorKind::InvalidInput)) {
        tracing::warn!(error=%e, "trailer CR not followed by LF");
        break;
    }
    return Err(e.into());
}

Prevention

When it happens

Trigger: A trailer block like "0\r\nbad\r\r\n" where a trailer line's CR is followed by another CR or other byte instead of LF, or any trailer whose terminator lost its LF.

Common situations: A proxy that mangles newlines in trailers; a custom encoder that writes "\r" only at end of each trailer; corrupted bytes near the end of the body.

Related errors


AI-assisted analysis of hyperium/hyper@084473f728 (2026-08-06). Data as JSON: /data/errors/aaecc72085fd8a31.json. Report an issue: GitHub.