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
- Increase the grace period in Rocket.toml: [default.shutdown] grace = 30
- Notify clients to disconnect before shutdown (e.g. close websockets in a shutdown fairing) so connections drain faster
- Pair a long grace with an orchestrator-level terminationGracePeriodSeconds larger than Rocket's grace
- 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
- Size shutdown.grace to your slowest legitimate request, not to 5s defaults
- Send clients a signal (Connection: close, websocket close frame) before initiating shutdown
- Keep orchestrator termination grace above Rocket's grace so SIGKILL never cuts it short
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.