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

Partial header

Error message

Partial header

What it means

Thrown by decode_trailers (src/proto/h1/decode.rs:672) when httparse::parse_headers returns Status::Partial on the buffered trailer block — meaning the accumulated bytes do not form a complete, properly-terminated set of headers (no final blank-line CRLF that the parser accepts as Complete). Reported as io::ErrorKind::InvalidInput.

Source

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

                    }
                };

                let value = match HeaderValue::from_bytes(header.value) {
                    Ok(value) => value,
                    Err(_) => {
                        return Err(io::Error::new(
                            io::ErrorKind::InvalidInput,
                            format!("Invalid header value: {:?}", &header),
                        ));
                    }
                };

                trailers.append(name, value);
            }

            Ok(trailers)
        }
        Ok(httparse::Status::Partial) => Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "Partial header",
        )),
        Err(e) => Err(io::Error::new(io::ErrorKind::InvalidInput, e)),
    }
}

#[derive(Debug)]
struct IncompleteBody;

impl fmt::Display for IncompleteBody {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "end of file before message length reached")
    }
}

impl StdError for IncompleteBody {}

View on GitHub (pinned to 084473f728)

Solutions

  1. Raise h1_max_header_size via the builder if a legitimate trailer is larger than the 16 KiB default.
  2. Confirm the sender writes the trailer-terminating blank line ("\r\n") after the last trailer.
  3. Split oversized trailer values or move them into the body.
  4. Capture the trailer bytes and run httparse on them locally to see exactly where parsing goes partial.

Example fix

// before: default 16 KiB trailer cap, oversized trailer truncated
let srv = Server::bind(&addr).serve(make_svc);

// after: raise trailer byte limit
let srv = Server::bind(&addr)
    .http1_max_header_size(1024 * 64)
    .serve(make_svc);
Defensive patterns

Strategy: validation

Validate before calling

// Size trailer budgets to fit within h1_max_header_size (default 16 KiB).
let max_header_size = 1024 * 64; // raise the cap to fit legitimate trailers
let server = hyper::server::Server::bind(&addr)
    .http1_max_header_size(max_header_size)
    .serve(make_svc);
// On send: ensure each trailer line + the final blank line fit the cap.
assert!(trailer_block.len() < max_header_size);

Try / catch

Some(Err(e)) => {
    if e.to_string().contains("Partial header") {
        tracing::warn!(error=%e, "trailer block parsed as partial; check h1_max_header_size / terminator");
        break;
    }
    return Err(e.into());
}

Prevention

When it happens

Trigger: A trailer block that is internally cut off or mis-sequenced so httparse sees it as partial — e.g. trailers that reached the byte-size cap (TRAILER_LIMIT / h1_max_header_size) mid-header, or framing where the terminating blank line is malformed. The state machine reached End but the bytes collected in trailers_buf don't parse cleanly.

Common situations: A trailer that exceeds the trailer byte cap (h1_max_header_size, default 16 KiB) causing the buffer to be cut mid-header; a peer that emits trailers without the final blank line; interference from a proxy that rewrites the trailer section.

Related errors


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