dbt-labs/dbt-core · error

{e} {:?}

Error message

{e}
{:?}

What it means

Handle::current() returns the runtime handle for the calling thread, but only if the thread is inside a runtime context (a worker thread or inside Handle::enter). If try_current() fails — meaning no runtime is associated with the thread — this panics; in debug builds it also captures and prints a forced backtrace to aid diagnosis.

Source

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

        Handle {
            inner: Arc::new(inner),
        }
    }

    /// 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. Use Handle::try_current() and handle the Err case instead of panicking.
  2. Only call current() on threads inside the runtime, or enter the runtime first: `let _g = handle.enter();` on the foreign thread.
  3. Pass the Handle explicitly into functions/threads instead of relying on thread-local context.
  4. Annotate async tests with #[tokio::test] and run blocking work via spawn_blocking, not std threads, when runtime APIs are needed.

Example fix

// before
let handle = Handle::current(); // panics off-runtime

// after
let handle = match Handle::try_current() {
    Ok(h) => h,
    Err(_) => return fallback_handle_or_default(),
};
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check before calling current()
if Handle::try_current().is_err() {
    eprintln!("not inside a runtime context; skipping Handle::current()");
    return;
}

Type guard

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

Try / catch

let handle = match Handle::try_current() {
    Ok(h) => h,
    Err(e) => {
        log::warn!("no runtime on this thread: {e}; using fallback");
        return fallback();
    }
};

Prevention

When it happens

Trigger: Calling Handle::current() from a thread with no runtime context: plain std::thread::spawn outside the runtime, blocking code moved off-runtime without re-entering, calling it in main() before starting the runtime, or calling it inside block_in_place after the runtime was shut down.

Common situations: Using Handle::current() in a helper invoked from both async and sync code; spawning a std thread that then tries to spawn async tasks via a runtime handle; running tests with #[test] instead of #[tokio::test] while calling runtime-aware APIs.

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/0cf11acc7ae1d121. Report an issue: GitHub.