hyperium/hyper · error · hyper::Error

connection closed before message completed

Error message

connection closed before message completed

What it means

Thrown via Error::new_incomplete() (src/error.rs:367, Kind::IncompleteMessage), gated behind feature="http1". It means the underlying IO reported EOF (a closed socket) while hyper's HTTP/1 state machine still expected more of the message — either the head or the body was only partially received. Detect it with Error::is_incomplete_message(). The hyper docs note common causes: a request sent on a connection whose next read is EOF (server closed an idle conn), a body cut short before Content-Length/chunk end, or a client that half-closes while you wait to respond.

Source

Thrown at src/error.rs:367

        None
    }

    #[cfg(all(any(feature = "client", feature = "server"), feature = "http2"))]
    pub(super) fn h2_reason(&self) -> h2::Reason {
        // Find an h2::Reason somewhere in the cause stack, if it exists,
        // otherwise assume an INTERNAL_ERROR.
        self.find_source::<h2::Error>()
            .and_then(|h2_err| h2_err.reason())
            .unwrap_or(h2::Reason::INTERNAL_ERROR)
    }

    pub(super) fn new_canceled() -> Error {
        Error::new(Kind::Canceled)
    }

    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
    pub(super) fn new_incomplete() -> Error {
        Error::new(Kind::IncompleteMessage)
    }

    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
    pub(super) fn new_too_large() -> Error {
        Error::new(Kind::Parse(Parse::TooLarge))
    }

    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
    pub(super) fn new_version_h2() -> Error {
        Error::new(Kind::Parse(Parse::VersionH2))
    }

    #[cfg(all(any(feature = "client", feature = "server"), feature = "http1"))]
    pub(super) fn new_unexpected_message() -> Error {
        Error::new(Kind::UnexpectedMessage)
    }

    #[cfg(all(

View on GitHub (pinned to 084473f728)

Solutions

  1. Detect with Error::is_incomplete_message() and retry idempotent requests on a new connection rather than failing the user.
  2. If you are a server whose client half-closes while awaiting a response, enable http1 half_close on the server Builder so the connection can still complete.
  3. Tune keep-alive/idle timeouts on both peers so neither closes a connection the other is about to reuse, and drain bodies fully before returning.

Example fix

// before: incomplete message aborts the whole operation
let resp = client.get(uri).await?;

// after: retry on a fresh connection when the old one was cut mid-message
async fn fetch(client: &Client<...>, uri: Uri) -> Result<Response<Body>, hyper::Error> {
    for attempt in 0..2u8 {
        match client.get(uri.clone()).await {
            Err(e) if e.is_incomplete_message() && attempt == 0 => continue,
            res => return res,
        }
    }
    unreachable!()
}
Defensive patterns

Strategy: retry

Type guard

fn is_incomplete(err: &hyper::Error) -> bool {
    err.is_incomplete_message()
}

Try / catch

match client.get(uri.clone()).await {
    Err(e) if e.is_incomplete_message() && idempotent => { /* retry on new connection */ }
    other => other,
}

Prevention

When it happens

Trigger: Reading the request/response head hits EOF before the terminating CRLF CRLF (proto/h1/io.rs:214, proto/h1/conn.rs:473 and :504); the dispatch sender is told the connection ended with an in-flight message (proto/h1/dispatch.rs:551). Happens when the peer closes the TCP stream partway through a request or response.

Common situations: An upstream reverse proxy/load balancer closes an idle keep-alive connection just as you send the next request; a client sends headers then closes the write half while the server is still computing a response (needs half_close enabled); a flaky mobile network drops the socket mid-body; a server with aggressive idle timeouts.

Related errors


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