tokio-rs/tokio · error · io::Error

A Tokio 1.x context was found, but it is being shutdown.

Error message

A Tokio 1.x context was found, but it is being shutdown.

What it means

Registration::gone() returns this io::Error when an I/O resource operation is attempted against an I/O driver that has been shut down. It indicates the ScheduledIo/Registration backing a socket/file is no longer connected to a live driver, so async I/O cannot proceed.

Source

Thrown at tokio/src/runtime/io/registration.rs:255

        self.handle.driver().io()
    }
}

impl Drop for Registration {
    fn drop(&mut self) {
        // It is possible for a cycle to be created between wakers stored in
        // `ScheduledIo` instances and `Arc<driver::Inner>`. To break this
        // cycle, wakers are cleared. This is an imperfect solution as it is
        // possible to store a `Registration` in a waker. In this case, the
        // cycle would remain.
        //
        // See tokio-rs/tokio#3481 for more details.
        self.shared.clear_wakers();
    }
}

fn gone() -> io::Error {
    io::Error::new(
        io::ErrorKind::Other,
        crate::util::error::RUNTIME_SHUTTING_DOWN_ERROR,
    )
}

View on GitHub (pinned to 625954f365)

Solutions

  1. Keep the runtime alive for the entire lifetime of any owned I/O resource.
  2. Close/drop sockets before dropping the runtime.
  3. Detect shutdown via the returned error and propagate it instead of retrying.
  4. Avoid capturing I/O resources in long-lived 'static spawns tied to a shorter-lived runtime.

Example fix

// before
let stream = TcpStream::connect(...).await?;
drop(runtime);
stream.read_buf(&mut buf).await?; // returns gone()
// after
let n = stream.read_buf(&mut buf).await;
match n {
    Err(e) if e.to_string().contains("shutting down") => return,
    _ => {},
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure resource lifetime <= runtime lifetime by design; no cheap runtime check exists.
// Optionally: assert the runtime guard is held in your API entry points.

Try / catch

match resource.read_buf(&mut buf).await {
    Ok(_) => {},
    Err(e) if e.to_string().contains("shutting down") => {
        // driver gone: clean up and exit
        return;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Using an async TcpStream/UdpStream/UnixStream after its runtime's I/O driver has shut down; registering or reading/writing on a resource whose driver Arc has gone away.

Common situations: Holding an I/O handle past runtime drop; moving a TcpStream into a 'static task that outlives the runtime; test harnesses that drop the runtime while connections linger.

Related errors


AI-assisted analysis of tokio-rs/tokio@625954f365 (2026-08-11). Data as JSON: /api/errors/6ffff9cf35025b67. Report an issue: GitHub.