tokio-rs/tokio · critical

A Tokio 1.x context was found, but timers are disabled. Call

Error message

A Tokio 1.x context was found, but timers are disabled. Call `enable_time` on the runtime builder to enable timers.

What it means

Driver::time() calls .expect(...) when self.time is None — the runtime was built without the time driver (no enable_time / not enable_all), yet code uses tokio::time APIs (sleep, interval, timeout).

Source

Thrown at tokio/src/runtime/driver.rs:115

    cfg_signal_internal_and_unix! {
        #[track_caller]
        pub(crate) fn signal(&self) -> &crate::runtime::signal::Handle {
            self.signal
                .as_ref()
                .expect("A Tokio 1.x context was found, but IO is disabled. Call `enable_io` on the runtime builder to enable IO.")
        }
    }

    cfg_time! {
        /// Returns a reference to the time driver handle.
        ///
        /// Panics if no time driver is present.
        #[track_caller]
        pub(crate) fn time(&self) -> &crate::runtime::time::Handle {
            self.time
                .as_ref()
                .expect("A Tokio 1.x context was found, but timers are disabled. Call `enable_time` on the runtime builder to enable timers.")
        }

        #[cfg(tokio_unstable)]
        pub(crate) fn with_time<F, R>(&self, f: F) -> R
        where
            F: FnOnce(Option<&crate::runtime::time::Handle>) -> R,
        {
            f(self.time.as_ref())
        }

        pub(crate) fn clock(&self) -> &Clock {
            &self.clock
        }
    }
}

// ===== io driver =====

View on GitHub (pinned to 7d0d729d8f)

Solutions

  1. Call .enable_all() (or .enable_time()) on the Builder.
  2. Don't call tokio::time::* on time-disabled runtimes; route timer usage to a full runtime.
  3. Audit Builder config and ensure enable_time when timers are used.
  4. If you truly need no timers, eliminate all sleep/timeout/interval usages in that runtime's scope.

Example fix

// before
let rt = Builder::new_current_thread().enable_io().build()?;
rt.block_on(async { tokio::time::sleep(Duration::from_secs(1)).await; }); // panics
// after
let rt = Builder::new_current_thread().enable_all().build()?;
rt.block_on(async { tokio::time::sleep(Duration::from_secs(1)).await; });
Defensive patterns

Strategy: validation

Validate before calling

let rt = tokio::runtime::Builder::new_current_thread()
    .enable_all() // ensures time driver is on
    .build()?;

Try / catch

// Macro panics; manual build surfaces error:
match Builder::new_current_thread().enable_all().build() {
    Ok(rt) => rt.block_on(async { tokio::time::sleep(...).await }),
    Err(e) => { eprintln!("no time driver: {e}"); std::process::exit(1); }
}

Prevention

When it happens

Trigger: Calling sleep/interval/timeout on a runtime whose Builder lacks enable_time(); using a minimal Builder for compute then awaiting a timer.

Common situations: Custom Builder omitting enable_all; intentionally disabling timers then accidentally calling sleep; test harness with stripped-down runtime.

Related errors


AI-assisted analysis of tokio-rs/tokio@7d0d729d8f (2026-08-11). Data as JSON: /api/errors/0dd6cfb940685261. Report an issue: GitHub.