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
- Use Handle::try_current() and handle the Err case instead of panicking.
- Only call current() on threads inside the runtime, or enter the runtime first: `let _g = handle.enter();` on the foreign thread.
- Pass the Handle explicitly into functions/threads instead of relying on thread-local context.
- 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
- Prefer try_current() over current() in library/API-boundary code.
- Spawn async work with tokio::spawn / spawn_blocking rather than std::thread when runtime context is needed.
- Pass Handle explicitly to functions that may run on foreign threads.
- Use #[tokio::test] so tests execute inside a runtime context.
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
- {e}
- `EnterGuard` values dropped out of order. Guards returned by
- inconsistent park state; actual = {actual}
- inconsistent park_timeout state; actual = {actual}
- Cannot drop a runtime in a context where blocking is not all
AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07).
Data as JSON: /api/errors/0cf11acc7ae1d121.
Report an issue: GitHub.