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

Invalid header value: {:?}

Error message

Invalid header value: {:?}

What it means

Thrown by decode_trailers (src/proto/h1/decode.rs:660) when a trailer's value fails HeaderValue::from_bytes — HTTP field values may contain only visible ASCII plus spaces and tabs (no raw control bytes, no NUL). The offending header is formatted into the message via {:?}. Reported as io::ErrorKind::InvalidInput.

Source

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

    let res = httparse::parse_headers(buf, &mut headers);
    match res {
        Ok(httparse::Status::Complete((_, headers))) => {
            for header in headers {
                use std::convert::TryFrom;
                let name = match HeaderName::try_from(header.name) {
                    Ok(name) => name,
                    Err(_) => {
                        return Err(io::Error::new(
                            io::ErrorKind::InvalidInput,
                            format!("Invalid header name: {:?}", &header),
                        ));
                    }
                };

                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)),
    }
}

View on GitHub (pinned to 084473f728)

Solutions

  1. Base64- or percent-encode binary/UTF-8 trailer values before sending.
  2. Strip CR/LF/NUL and other control bytes from any dynamic trailer value.
  3. Keep trailer values to visible ASCII; move large/binary metadata into the body.

Example fix

// before: raw newline in trailer value
write!(w, "0\r\nx-log: a\nb\r\n\r\n").await?; // -> error 33

// after: encode the value
let v = base64::encode(b"a\nb");
write!(w, "0\r\nx-log: {}\r\n\r\n", v).await?;
Defensive patterns

Strategy: validation

Validate before calling

// Reject/encode non-ASCII or control bytes in trailer values before sending.
fn safe_trailer_value(v: &str) -> Option<String> {
    v.is_ascii() && v.bytes().all(|b| b == b'\t' || b == b' ' || (0x21..=0x7e).contains(&b))
        .then(|| v.to_string())
        .or_else(|| Some(base64::encode(v.as_bytes())))
}
write!(w, "0\r\nx-log: {}\r\n\r\n", safe_trailer_value(&raw).unwrap()).await?;

Type guard

fn is_printable_ascii_value(v: &str) -> bool {
    v.bytes().all(|b| b == b'\t' || b == b' ' || (0x21..=0x7e).contains(&b))
}

Try / catch

Some(Err(e)) => {
    if e.to_string().contains("Invalid header value") {
        tracing::warn!(error=%e, "peer sent trailer with illegal field value");
        break;
    }
    return Err(e.into());
}

Prevention

When it happens

Trigger: A trailer value containing a control byte, NUL, or a bare CR/LF not part of folded whitespace, e.g. "x-trace: abc\0def\r\n" or a value with a raw newline.

Common situations: Binary/UTF-8 data placed verbatim into a trailer; debug strings with embedded newlines; a logging proxy that copies unescaped stack traces into a trailer value.

Related errors


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