transact-rs/sqlx · critical

this functionality requires a Tokio context

Error message

this functionality requires a Tokio context

What it means

missing_rt() in sqlx-core/src/rt/mod.rs is sqlx's guard for runtime-abstract APIs. When the `_rt-tokio` feature IS enabled but the API is called outside any active Tokio runtime/context, sqlx panics with 'this functionality requires a Tokio context' (the second panic branch, 'one of the runtime features...' only fires when no runtime feature is compiled in). Many sqlx operations (pool creation, queries, timers) spawn on the async runtime and cannot run without one.

Source

Thrown at sqlx-core/src/rt/mod.rs:166

    cfg_if! {
        if #[cfg(feature = "_rt-async-io")] {
            async_io::block_on(f)
        } else if #[cfg(feature = "_rt-tokio")] {
            tokio::runtime::Builder::new_current_thread()
                .enable_all()
                .build()
                .expect("failed to start Tokio runtime")
                .block_on(f)
        } else {
            missing_rt(f)
        }
    }
}

#[track_caller]
pub const fn missing_rt<T>(_unused: T) -> ! {
    if cfg!(feature = "_rt-tokio") {
        panic!("this functionality requires a Tokio context")
    }

    panic!("one of the `runtime` features of SQLx must be enabled")
}

impl<T: Send + 'static> Future for JoinHandle<T> {
    type Output = T;

    #[track_caller]
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        match &mut *self {
            #[cfg(feature = "_rt-async-std")]
            Self::AsyncStd(handle) => Pin::new(handle).poll(cx),

            #[cfg(feature = "_rt-async-task")]
            Self::AsyncTask(task) => Pin::new(task)
                .as_pin_mut()
                .expect("BUG: task taken")

View on GitHub (pinned to 03af8bcc57)

Solutions

  1. Wrap the entry point in a Tokio runtime: `#[tokio::main] async fn main()` or build one manually with `tokio::runtime::Runtime::new()?.block_on(...)`.
  2. Annotate async tests with `#[tokio::test]` instead of `#[test]`.
  3. If calling from sync code, use `SomePool::connect_lazy` plus a runtime-owned handle, or move DB work onto a Tokio worker via `runtime.spawn(...)`.
  4. If your app uses a non-Tokio runtime, switch the sqlx feature to a matching one — but note current sqlx effectively requires Tokio for runtime-dependent APIs.

Example fix

// before
fn main() {
    let pool = PgPool::connect("postgres://..."); // panic: no Tokio context
}

// after
#[tokio::main]
async fn main() {
    let pool = PgPool::connect("postgres://...").await.unwrap();
}
Defensive patterns

Strategy: validation

Validate before calling

// Ensure a Tokio context exists before DB work:
assert!(
    tokio::runtime::Handle::try_current().is_ok(),
    "sqlx calls require a Tokio runtime context"
);

Prevention

When it happens

Trigger: Calling `PgPool::connect`, `Pool::acquire`, `query.fetch...` etc. from code not running inside `#[tokio::main]`/`tokio::runtime::Runtime::block_on` — e.g. from a synchronous main, a blocking thread, a C FFI callback, or tests without the `#[tokio::test]` attribute.

Common situations: Rust binaries with plain `fn main()` calling sqlx directly; integration tests missing tokio attributes; embedding sqlx in a non-Tokio app (async-std, smol) while only the tokio feature is enabled.

Related errors


AI-assisted analysis of transact-rs/sqlx@03af8bcc57 (2026-09-03). Data as JSON: /api/errors/f1f7d2d675a65aeb. Report an issue: GitHub.