hyperium/hyper · warning · hyper::Error
channel closed
Error message
channel closed
What it means
Thrown via Error::new_closed() (src/error.rs:398, Kind::ChannelClosed). It indicates the other end of an internal mpsc/watch channel was dropped: either the dispatch task (client side) or the body sender/receiver. Detect with Error::is_closed(). It usually means a producer or consumer went away — for a client it can mean the connection task ended; for a body it means the reader stopped polling.
Source
Thrown at src/error.rs:398
#[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
pub(super) fn new_unexpected_message() -> Error {
Error::new(Kind::UnexpectedMessage)
}
#[cfg(all(
any(feature = "client", feature = "server"),
any(feature = "http1", feature = "http2")
))]
pub(super) fn new_io(cause: std::io::Error) -> Error {
Error::new(Kind::Io).with(cause)
}
#[cfg(any(
all(feature = "http1", any(feature = "client", feature = "server")),
all(feature = "http2", feature = "client")
))]
pub(super) fn new_closed() -> Error {
Error::new(Kind::ChannelClosed)
}
#[cfg(all(
any(feature = "client", feature = "server"),
any(feature = "http1", feature = "http2")
))]
pub(super) fn new_body<E: Into<Cause>>(cause: E) -> Error {
Error::new(Kind::Body).with(cause)
}
#[cfg(all(
any(feature = "client", feature = "server"),
any(feature = "http1", feature = "http2")
))]
pub(super) fn new_body_write<E: Into<Cause>>(cause: E) -> Error {
Error::new(Kind::BodyWrite).with(cause)
}
View on GitHub (pinned to 084473f728)
Solutions
- Detect with Error::is_closed() and create a brand-new connection/client rather than reusing the dead handle.
- Ensure the future that drives the connection (the one yielded by client::conn::http1::handshake) stays alive for as long as you use the SendRequest handle.
- For streaming bodies, make sure the body future isn't dropped prematurely, and finish sending (or abort intentionally) before the consumer disappears.
Example fix
// before: reuse a client handle whose connection task already ended
let resp = sender.send_request(req).await?; // Err: channel closed
// after: rebuild the connection when the channel is gone
match sender.send_request(req).await {
Err(e) if e.is_closed() => {
let (new_sender, conn) = hyper::client::conn::http1::handshake(stream).await?;
tokio::spawn(conn);
// retry with new_sender ...
}
other => other,
} Defensive patterns
Strategy: fallback
Type guard
fn is_channel_closed(err: &hyper::Error) -> bool {
err.is_closed()
} Try / catch
match sender.send_request(req).await {
Err(e) if e.is_closed() => {
let (sender, conn) = hyper::client::conn::http1::handshake(new_stream()).await?;
tokio::spawn(conn);
// retry with the fresh sender
}
other => return other,
} Prevention
- Spawn the connection future from handshake immediately so it outlives the handle.
- Detect is_closed() and rebuild the connection rather than reusing a dead handle.
- Keep body producers alive until they finish or are intentionally aborted.
When it happens
Trigger: Client send_request fails because the dispatch sender was dropped (client/dispatch.rs:79); a Body sender's channel receiver was dropped (body/incoming.rs:367/374/391/399/401); an HTTP/2 client stream's channel closed (client/conn/http2.rs:99).
Common situations: The connection-driving future was dropped (task cancelled) while you still hold a SendRequest handle; a streaming body producer was dropped before sending the end; a client is reused after its background connection task already finished; backpressure where the consumer is gone.
Related errors
- dispatch task is gone
- operation was canceled
- error reading a body from connection
- error writing a body to connection
- user body write aborted
AI-assisted analysis of hyperium/hyper@084473f728 (2026-08-06).
Data as JSON: /data/errors/a679c309d2885974.json.
Report an issue: GitHub.