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
- Switch to Handle::try_current() and handle the no-runtime case gracefully.
- Enter the runtime on foreign threads before calling runtime APIs (`handle.enter()` guard).
- Thread the Handle through function parameters or an app-level context instead of thread-locals.
- 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
- Call Handle::current() only from within runtime threads or an active handle.enter() scope.
- Keep a globally stored Handle (set once after runtime startup) for code on arbitrary threads.
- Avoid calling runtime APIs in Drop impls that may run after runtime shutdown.
- Run debug builds during development to get backtraces pinpointing off-runtime call sites.
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
- {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/fde302cf04c980f3.
Report an issue: GitHub.