hyperium/hyper · warning · hyper::Error
read header from client timeout
Error message
read header from client timeout
What it means
Thrown via Error::new_header_timeout() (src/error.rs:437, Kind::HeaderTimeout), HTTP/1 server only. It fires when the client does not send the full request head (request line + headers) within header_read_timeout — the timer (which requires a Timer set via Builder::timer) elapses while hyper is still Pending on the head parse (proto/h1/conn.rs:264). The default is 30s (server/conn/http1.rs:250). Detect with Error::is_timeout().
Source
Thrown at src/error.rs:437
feature = "ffi"
))]
pub(super) fn new_body_write_aborted() -> Error {
Error::new(Kind::User(User::BodyWriteAborted))
}
fn new_user(user: User) -> Error {
Error::new(Kind::User(user))
}
#[cfg(any(feature = "http1", feature = "http2"))]
#[cfg(feature = "server")]
pub(super) fn new_user_header() -> Error {
Error::new_user(User::UnexpectedHeader)
}
#[cfg(all(feature = "http1", feature = "server"))]
pub(super) fn new_header_timeout() -> Error {
Error::new(Kind::HeaderTimeout)
}
#[cfg(feature = "http1")]
#[cfg(feature = "server")]
pub(super) fn new_user_unsupported_status_code() -> Error {
Error::new_user(User::UnsupportedStatusCode)
}
pub(super) fn new_user_no_upgrade() -> Error {
Error::new_user(User::NoUpgrade)
}
#[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
pub(super) fn new_user_manual_upgrade() -> Error {
Error::new_user(User::ManualUpgrade)
}
#[cfg(any(View on GitHub (pinned to 084473f728)
Solutions
- Ensure a Timer is installed (Builder::timer with tokio time) so the timeout actually takes effect, and pick a sane header_read_timeout for your clients.
- If legitimate slow clients trip it, raise header_read_timeout (or disable it) — but keep some limit to resist slowloris.
- Put a connection-level idle/read timeout in front (reverse proxy) as defense in depth.
Example fix
// before: header_read_timeout configured but no Timer => panic, or no effect let mut http = Http::new(); http.header_read_timeout(std::time::Duration::from_secs(30)); // after: install the tokio Timer so the timeout is enforced let mut http = Http::new(); http.with_upgrades(); http.timer(tokio_compat::Timer::new()); http.header_read_timeout(std::time::Duration::from_secs(30));
Defensive patterns
Strategy: try-catch
Validate before calling
// Confirm a Timer is installed before the server runs, otherwise the
// timeout silently does nothing (or panics if set without a Timer).
fn assert_timer_configured(b: &hyper::server::conn::http1::Builder) {
// Builder::timer must have been called for header_read_timeout to take effect
let _ = b; // your framework wrapper should expose/require the timer
} Type guard
fn is_header_timeout(err: &hyper::Error) -> bool {
err.is_timeout()
} Try / catch
if let Err(e) = conn_fut.await {
if e.is_timeout() {
// client was too slow sending headers; just close
} else {
tracing::error!("conn error: {e}");
}
} Prevention
- Always call Builder::timer(...) when you set header_read_timeout, or it panics/no-ops.
- Pick a header_read_timeout that tolerates your slowest legitimate client.
- Layer a reverse-proxy idle timeout as defense-in-depth against slowloris.
When it happens
Trigger: A client opens a TCP/TLS connection but never sends (or only slowly sends) the request line+headers; a port scanner / health check that opens a socket and idles; slowloris-style attack. The header timer at conn.rs:219-234 fires and poll_read_head returns Err(new_header_timeout()) at conn.rs:264.
Common situations: Forgotten to call Builder::timer (the timeout is a no-op without a Timer, and configuring it without a timer panics — see server/conn/http1.rs:346/480); load balancer health checks that hold connections open; misbehaving or malicious slow clients; default 30s too short for very slow uploaders.
Related errors
- operation was canceled
- connection closed before message completed
- message head is too large
- invalid HTTP version parsed (found HTTP2 preface)
- received unexpected message from connection
AI-assisted analysis of hyperium/hyper@084473f728 (2026-08-06).
Data as JSON: /data/errors/f94a7ecf775e57ed.json.
Report an issue: GitHub.