tokio-rs/tokio · critical

there is no signal driver running, must be called from the…

Error message

there is no signal driver running, must be called from the context of Tokio runtime

What it means

Driver::signal() calls .expect(...) when self.signal is None — the runtime has no signal driver. Signal APIs (ctrl_c, unix::signal) require the signal driver, which is only enabled with enable_all or on platforms with cfg_signal_internal_and_unix.

Solutions

  1. Use .enable_all() on the Builder so the signal driver is created.
  2. On non-Unix/non-supported targets, avoid signal APIs and handle shutdown differently.
  3. Ensure signal calls originate from within the runtime context (enter()/block_on).
  4. Verify cfg_signal_internal_and_unix is active for your target.

Example fix

// before
let rt = Builder::new_current_thread().build()?;
rt.block_on(async { signal::ctrl_c().await }); // panics
// after
let rt = Builder::new_current_thread().enable_all().build()?;
rt.block_on(async { signal::ctrl_c().await });
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the signal driver is present by enabling all drivers:
let rt = tokio::runtime::Builder::new_current_thread()
    .enable_all() // includes signal driver on Unix
    .build()?;

Try / catch

// Panic in macro path; manual build lets you handle:
match Builder::new_current_thread().enable_all().build() {
    Ok(rt) => rt.block_on(async { signal::ctrl_c().await }),
    Err(e) => { eprintln!("no signal driver: {e}"); std::process::exit(1); }
}

Prevention

When it happens

Trigger: Calling signal APIs on a runtime built without enable_io (the signal driver depends on the I/O driver) or on a platform/configuration where cfg_signal_internal_and_unix is off.

Common situations: Builder with only enable_time(); calling ctrl_c from a manually-built current-thread runtime that skipped enable_all; target without Unix signal support.

Related errors


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

Appendix: source

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

        self.io.unpark();
    }

    cfg_io_driver! {
        #[track_caller]
        pub(crate) fn io(&self) -> &crate::runtime::io::Handle {
            self.io
                .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_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,

View on GitHub (pinned to 8146318256)