{"id":"c75548b51a89a801","repo":"hyperium/hyper","slug":"invalid-header-name","errorCode":null,"errorMessage":"Invalid header name: {:?}","messagePattern":"Invalid header name: (.+?)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"src/proto/h1/decode.rs","lineNumber":650,"sourceCode":"                \"Invalid chunk end LF\",\n            ))),\n        }\n    }\n}\n\n// TODO: disallow Transfer-Encoding, Content-Length, Trailer, etc in trailers ??\nfn decode_trailers(buf: &mut BytesMut, count: usize) -> Result<HeaderMap, io::Error> {\n    let mut trailers = HeaderMap::new();\n    let mut headers = vec![httparse::EMPTY_HEADER; count];\n    let res = httparse::parse_headers(buf, &mut headers);\n    match res {\n        Ok(httparse::Status::Complete((_, headers))) => {\n            for header in headers {\n                use std::convert::TryFrom;\n                let name = match HeaderName::try_from(header.name) {\n                    Ok(name) => name,\n                    Err(_) => {\n                        return Err(io::Error::new(\n                            io::ErrorKind::InvalidInput,\n                            format!(\"Invalid header name: {:?}\", &header),\n                        ));\n                    }\n                };\n\n                let value = match HeaderValue::from_bytes(header.value) {\n                    Ok(value) => value,\n                    Err(_) => {\n                        return Err(io::Error::new(\n                            io::ErrorKind::InvalidInput,\n                            format!(\"Invalid header value: {:?}\", &header),\n                        ));\n                    }\n                };\n\n                trailers.append(name, value);\n            }","sourceCodeStart":632,"sourceCodeEnd":668,"githubUrl":"https://github.com/hyperium/hyper/blob/084473f728f9d07b3be5845475aa2f62ed9ff579/src/proto/h1/decode.rs#L632-L668","documentation":"Thrown by decode_trailers (src/proto/h1/decode.rs:650) when a parsed trailer header's name fails HeaderName::try_from — i.e. it contains characters not allowed in HTTP field names (only visible VCHAR excluding ':' is permitted). The offending header is included in the message via {:?}. Reported as io::ErrorKind::InvalidInput.","triggerScenarios":"A trailer block whose header name is illegal, e.g. \"X Bad: 1\\r\\n\" (space in name), \"X-Trace\\r\\n\" (no colon treated as invalid by httparse and surfaced here), or a non-ASCII field name like a UTF-8 label.","commonSituations":"A proxy injecting malformed trailer names; UTF-8 metadata from an upstream system placed verbatim into trailers; gRPC metadata keys that violate HTTP token rules (e.g. uppercase or spaces).","solutions":["Sanitize trailer names on the sending side: ASCII tokens, no spaces, no colon, typically lower-case kebab.","Reject/redirect upstream metadata that isn't a valid HTTP field name instead of forwarding it as a trailer.","Log the offending header (it appears in the error) to identify which peer/component produced it."],"exampleFix":"// before: forwarding arbitrary metadata as a trailer\nlet name = format!(\"{}\", raw_key); // may contain spaces/unicode\nwrite!(w, \"0\\r\\n{}: {}\\r\\n\\r\\n\", name, val).await?; // -> error 32\n\n// after: validate/sanitize the field name\nfn trailer_name(raw: &str) -> Option<&str> {\n    let r = raw.as_bytes();\n    r.iter().all(|b| b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_'))\n        .then_some(raw)\n}\nif let Some(n) = trailer_name(&raw_key) {\n    write!(w, \"0\\r\\n{}: {}\\r\\n\\r\\n\", n, val).await?;\n}","handlingStrategy":"validation","validationCode":"// Validate trailer names before emitting them: HTTP token chars only.\nfn valid_trailer_name(name: &str) -> bool {\n    !name.is_empty() && name.as_bytes().iter().all(|b| {\n        matches!(b,\n            b'!' | b'#' | b'$' | b'%' | b'&' | b'\\'' | b'*' | b'+'\n            | b'-' | b'.' | b'^' | b'_' | b'`' | b'|' | b'~'\n            | b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z')\n    })\n}\nif valid_trailer_name(&key) {\n    write!(w, \"0\\r\\n{}: {}\\r\\n\\r\\n\", key, val).await?;\n}","typeGuard":"fn valid_trailer_name(name: &str) -> bool {\n    !name.is_empty() && name.as_bytes().iter().all(|b| {\n        matches!(b,\n            b'!' | b'#'..=b'\\'' | b'*' | b'+' | b'-' | b'.' | b'^'..=b'`' | b'|' | b'~'\n            | b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z')\n    })\n}","tryCatchPattern":"Some(Err(e)) => {\n    if e.to_string().contains(\"Invalid header name\") {\n        tracing::warn!(error=%e, \"peer sent trailer with illegal field name\");\n        break;\n    }\n    return Err(e.into());\n}","preventionTips":["Restrict trailer names to RFC 7230 token characters; reject others at the trust boundary.","Don't forward arbitrary upstream metadata as trailers; map/sanitize first.","Log the offending header shown in the error to locate the misbehaving peer."],"tags":["http","http1","chunked","trailers","headers","hyper","rust"],"analyzedSha":"084473f728f9d07b3be5845475aa2f62ed9ff579","analyzedAt":"2026-08-06T01:20:18.522Z","schemaVersion":2}