rwf2/Rocket · warning · io::Error

TimedOut

TimedOut

Error message

shutdown grace period elapsed

What it means

Internal runtime error from Rocket's shutdown machinery (core/lib/src/listener/cancellable.rs): during graceful shutdown, each connection is given the configured grace period to finish; when it elapses with I/O still pending, the cancellable I/O wrapper returns io::ErrorKind::TimedOut with message 'shutdown grace period elapsed'. It signals forced termination of a still-active connection at shutdown, not a request-handling failure.

Source

Thrown at core/lib/src/listener/cancellable.rs:46

    Grace,
    /// Grace has elapsed. Shutdown connections. After `Shutdown`, force close.
    Mercy,
}

pub trait CancellableExt: Sized {
    fn cancellable(self, stages: Stages) -> Cancellable<Self> {
        Cancellable {
            io: Some(self),
            state: State::Active,
            stages,
        }
    }
}

impl<T> CancellableExt for T { }

fn time_out() -> io::Error {
    io::Error::new(io::ErrorKind::TimedOut, "shutdown grace period elapsed")
}

fn gone() -> io::Error {
    io::Error::new(io::ErrorKind::BrokenPipe, "I/O driver terminated")
}

impl<I: AsyncCancel> Cancellable<I> {
    pub fn inner(&self) -> Option<&I> {
        self.io.as_ref()
    }
}

pub trait AsyncCancel {
    fn poll_cancel(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>;
}

impl<T: AsyncWrite> AsyncCancel for T {
    fn poll_cancel(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {

View on GitHub (pinned to 3a54d079ae)

Solutions

  1. Increase the grace period in Rocket.toml: [default.shutdown] grace = 30
  2. Notify clients to disconnect before shutdown (e.g. close websockets in a shutdown fairing) so connections drain faster
  3. Pair a long grace with an orchestrator-level terminationGracePeriodSeconds larger than Rocket's grace
  4. Treat the error as expected during shutdown logging — filter it instead of alerting

Example fix

# before
# Rocket.toml (defaults: grace = 5s)

# after
# Rocket.toml
[default.shutdown]
grace = 30
# also raise k8s: terminationGracePeriodSeconds: 40
Defensive patterns

Strategy: try-catch

Try / catch

// in connection-handling code, treat TimedOut at shutdown as expected
match stream.read(&mut buf).await {
    Err(e) if e.kind() == io::ErrorKind::TimedOut
        && e.to_string().contains("grace period") => { /* shutting down: bail quietly */ break; }
    r => { /* normal handling */ }
}

Prevention

When it happens

Trigger: Rocket shutdown begins (SIGTERM/Ctrl-C, config.shutdown), a client keeps a connection open or a slow request is mid-flight, and shutdown.grace (default 5s) expires before that I/O completes. Subsequent read/write polls on that connection yield this error.

Common situations: Container orchestration sending SIGTERM while long uploads/downloads run; SSE/WebSocket-style long-lived connections still open at shutdown; slow mobile clients mid-request during a rolling deploy.

Related errors


AI-assisted analysis of rwf2/Rocket@3a54d079ae (2026-08-16). Data as JSON: /api/errors/92b121825de92e4b. Report an issue: GitHub.