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

chunk extensions over limit

Error message

chunk extensions over limit

What it means

Thrown in read_extension (src/proto/h1/decode.rs:444) when the running total of chunk-extension bytes (extensions_cnt) reaches CHUNKED_EXTENSIONS_LIMIT, a hard-coded 16 KiB cap applied across the whole body (see decode.rs:20). hyper does not parse extensions but bounds them to limit memory/wire abuse. Reported as io::ErrorKind::InvalidData.

Source

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

        extensions_cnt: &mut u64,
    ) -> Poll<Result<ChunkedState, io::Error>> {
        trace!("read_extension");
        // We don't care about extensions really at all. Just ignore them.
        // They "end" at the next CRLF.
        //
        // However, some implementations may not check for the CR, so to save
        // them from themselves, we reject extensions containing plain LF as
        // well.
        match byte!(rdr, cx) {
            b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
            b'\n' => Poll::Ready(Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "invalid chunk extension contains newline",
            ))),
            _ => {
                *extensions_cnt += 1;
                if *extensions_cnt >= CHUNKED_EXTENSIONS_LIMIT {
                    Poll::Ready(Err(io::Error::new(
                        io::ErrorKind::InvalidData,
                        "chunk extensions over limit",
                    )))
                } else {
                    Poll::Ready(Ok(ChunkedState::Extension))
                }
            } // no supported extensions
        }
    }
    fn read_size_lf<R: MemRead>(
        cx: &mut Context<'_>,
        rdr: &mut R,
        size: u64,
    ) -> Poll<Result<ChunkedState, io::Error>> {
        trace!("Chunk size is {:?}", size);
        match byte!(rdr, cx) {
            b'\n' => {
                if size == 0 {

View on GitHub (pinned to 084473f728)

Solutions

  1. Move large per-chunk metadata out of chunk extensions and into trailers or the body itself.
  2. Reduce the number of chunks (batch data) so extensions aren't repeated hundreds of times.
  3. If you are on the receiving side and the limit is a defense, leave it; if you genuinely need more, the limit is a const (CHUNKED_EXTENSIONS_LIMIT) and requires a hyper patch/rebuild.
  4. Front the service with a proxy that strips oversized extensions before they reach hyper.

Example fix

// before: stuffing trace context into every chunk extension
for part in parts {
    write!(w, "{:x};trace={}\r\n", part.len(), huge_trace).await?;
    w.write_all(&part).await?;
    write!(w, "\r\n").await?;
}

// after: send trace once as a trailer
write!(w, "0\r\ntrace: {}\r\n\r\n", huge_trace).await?;
Defensive patterns

Strategy: try-catch

Validate before calling

// When emitting many chunks, keep total extension bytes under 16 KiB.
const EXT_BUDGET: u64 = 16 * 1024;
let mut spent: u64 = 0;
for part in parts {
    let ext_len = trace_str.len() as u64;
    if spent + ext_len >= EXT_BUDGET {
        // stop emitting extensions; rely on a trailer instead
        write!(w, "{:x}\r\n", part.len()).await?;
    } else {
        write!(w, "{:x};t={}\r\n", part.len(), trace_str).await?;
        spent += ext_len;
    }
}

Try / catch

Some(Err(e)) => {
    if e.to_string().contains("chunk extensions over limit") {
        metrics::increment!("hyper.chunked.ext_over_limit");
        tracing::warn!(error=%e, "peer exceeded 16KB chunk-extension budget");
        break;
    }
    return Err(e.into());
}

Prevention

When it happens

Trigger: A chunked body whose combined extension text (everything after ';' on every chunk-size line, summed over all chunks) reaches 16384 bytes — e.g. many small chunks each carrying a large correlation-id extension, or one chunk with a >16KB extension blob.

Common situations: Tracing/observability proxies that attach large trace-state or baggage to every chunk; a sender that embeds base64 payloads in extensions; abuse/DoS where an attacker stuffs extensions to exhaust memory.

Related errors


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