dbt-labs/dbt-core · error

{e}

Error message

{e}

What it means

This is the release-build panic path of the same Handle::current() call: when try_current() reports no runtime handle for the current thread, the code panics with the underlying error message (without the backtrace that debug builds add). It signals that a runtime-context-dependent API was reached from a thread that is not part of any runtime.

Source

Thrown at crates/dbt-runtime/src/handle.rs:101

        }
    }

    /// Returns the handle set for the current thread.
    ///
    /// # Panics
    ///
    /// Panics if called outside a [`Handle::enter`] scope. In particular this
    /// panics inside a blocking task, by design — see the module docs. For a
    /// non-panicking version see [`Handle::try_current`].
    #[track_caller]
    pub fn current() -> Handle {
        match Handle::try_current() {
            Ok(handle) => handle,
            Err(e) => {
                if cfg!(debug_assertions) {
                    panic!("{e}\n{:?}", std::backtrace::Backtrace::force_capture());
                } else {
                    panic!("{e}");
                }
            }
        }
    }

    /// Returns the handle set for the current thread, if any.
    pub fn try_current() -> Result<Handle, TryCurrentError> {
        current::with_current(Handle::clone)
    }

    /// Sets this handle as the current one until the returned guard is dropped.
    pub fn enter(&self) -> EnterGuard<'_> {
        EnterGuard {
            _guard: current::try_set_current(self)
                .expect("cannot enter a runtime handle while the thread is shutting down"),
            _handle_lifetime: PhantomData,
        }
    }

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Switch to Handle::try_current() and handle the no-runtime case gracefully.
  2. Enter the runtime on foreign threads before calling runtime APIs (`handle.enter()` guard).
  3. Thread the Handle through function parameters or an app-level context instead of thread-locals.
  4. Reproduce with a debug build to get the backtrace identifying the off-runtime call site.

Example fix

// before
fn notify() {
    let h = Handle::current();
    h.spawn(async { ... });
}

// after
fn notify(handle: Option<Handle>) {
    if let Some(h) = handle.or_else(|| Handle::try_current().ok()) {
        h.spawn(async { ... });
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// production build has no backtrace; reproduce with debug asserts
cargo run --debug  # or check cfg!(debug_assertions) to get the backtrace variant

Type guard

fn runtime_available() -> bool {
    Handle::try_current().is_ok()
}

Try / catch

match Handle::try_current() {
    Ok(h) => h,
    Err(e) => {
        log::error!("Handle::current called off-runtime: {e}");
        std::process::exit(1); // or use a stored Handle
    }
}

Prevention

When it happens

Trigger: Invoking Handle::current() outside any runtime context — from a plain OS thread, from main() before the runtime is built, after runtime shutdown, or from blocking sections that left the runtime scope — in a release build (no debug_assertions).

Common situations: Mixing std::thread::spawn with runtime handles; libraries calling Handle::current() in Drop impls or destructors that may run on arbitrary threads; production (release) builds surfacing the same bug previously seen with backtrace in debug builds.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/fde302cf04c980f3. Report an issue: GitHub.