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
RegistrationSet::allocate returns this error when synced.is_shutdown is true. Allocation of a new ScheduledIo (the slot tracking readiness/interests for a freshly registered fd) is refused because the driver is already shut down — the resource cannot be tracked.
Source
Thrown at tokio/src/runtime/io/registration_set.rs:58
registrations: LinkedList::new(),
pending_release: Vec::with_capacity(NOTIFY_AFTER),
};
(set, synced)
}
pub(super) fn is_shutdown(&self, synced: &Synced) -> bool {
synced.is_shutdown
}
/// Returns `true` if there are registrations that need to be released
pub(super) fn needs_release(&self) -> bool {
self.num_pending_release.load(Acquire) != 0
}
pub(super) fn allocate(&self, synced: &mut Synced) -> io::Result<Arc<ScheduledIo>> {
if synced.is_shutdown {
return Err(io::Error::new(
io::ErrorKind::Other,
crate::util::error::RUNTIME_SHUTTING_DOWN_ERROR,
));
}
let ret = Arc::new(ScheduledIo::default());
// Push a ref into the list of all resources.
synced.registrations.push_front(ret.clone());
Ok(ret)
}
// Returns `true` if the caller should unblock the I/O driver to purge
// registrations pending release.
pub(super) fn deregister(&self, synced: &mut Synced, registration: &Arc<ScheduledIo>) -> bool {
synced.pending_release.push(registration.clone());
View on GitHub (pinned to 625954f365)
Solutions
- Construct all needed I/O resources before initiating runtime shutdown.
- Guard resource construction behind a check that the runtime/driver is still alive.
- Propagate the error rather than retrying — shutdown is terminal.
- Restructure so teardown waits for outstanding I/O resources to close.
Example fix
// before
runtime.enter();
let listener = TcpListener::bind(addr).await?; // may hit allocate() during shutdown
// after
if runtime.is_shutting_down() { return Err(shutdown()); }
let listener = TcpListener::bind(addr).await?; Defensive patterns
Strategy: validation
Validate before calling
// Construct all I/O resources up front, before initiating shutdown.
// If exposing a builder, refuse new resources once shutdown started:
if shutdown_started.load(Ordering::SeqCst) {
return Err(io::Error::new(io::ErrorKind::Other, "shutting down"));
}
TcpListener::bind(addr).await Try / catch
match TcpListener::bind(addr).await {
Ok(l) => l,
Err(e) if e.to_string().contains("shutting down") => return Err(e),
Err(e) => return Err(e),
} Prevention
- Build resources before runtime teardown begins.
- Gate resource construction with a shutdown flag.
- Treat 'driver shutdown' as terminal — do not retry.
When it happens
Trigger: Registering a new source with the I/O driver (tokio::net::* construction, AsyncFd registration) after driver shutdown was initiated.
Common situations: Creating a new TcpStream/UnixListener during runtime teardown; registering AsyncFd from a task running after shutdown started; nested runtimes where the inner driver is dropped first.
Related errors
- A Tokio 1.x context was found, but it is being shutdown.
- background task failed
- background task failed
- signal driver gone
- blocking pool shutting down
AI-assisted analysis of tokio-rs/tokio@625954f365 (2026-08-11).
Data as JSON: /api/errors/2c4acca028542480.
Report an issue: GitHub.