dbt-labs/dbt-core · error · syn::Error

The `unhandled_panic` option requires the `current_thread` r

Error message

The `unhandled_panic` option requires the `current_thread` runtime flavor. Use `#[{macro_name}(flavor = "current_thread")]`

What it means

This compile-time error is produced by the same `FinalConfig::build` step in crates/dbt-runtime-macros/src/entry.rs when the `unhandled_panic` option is set while the resolved runtime flavor is `Threaded` (multi_thread). The `unhandled_panic` knob (e.g. `unhandled_panic = "abort_runtime"`) is only implemented for the current_thread/Local runtime flavors in tokio, so the macro rejects the combination with a syn::Error anchored at the `unhandled_panic` argument's span.

Source

Thrown at crates/dbt-runtime-macros/src/entry.rs:249

        let start_paused = match (flavor, self.start_paused) {
            (F::Threaded, Some((_, start_paused_span))) => {
                let msg = format!(
                    "The `start_paused` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`",
                    self.macro_name(),
                );
                return Err(syn::Error::new(start_paused_span, msg));
            }
            (F::CurrentThread | F::Local, Some((start_paused, _))) => Some(start_paused),
            (_, None) => None,
        };

        let unhandled_panic = match (flavor, self.unhandled_panic) {
            (F::Threaded, Some((_, unhandled_panic_span))) => {
                let msg = format!(
                    "The `unhandled_panic` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`",
                    self.macro_name(),
                );
                return Err(syn::Error::new(unhandled_panic_span, msg));
            }
            (F::CurrentThread | F::Local, Some((unhandled_panic, _))) => Some(unhandled_panic),
            (_, None) => None,
        };

        Ok(FinalConfig {
            name: self.name.clone(),
            crate_name: self.crate_name.clone(),
            flavor,
            worker_threads,
            start_paused,
            unhandled_panic,
        })
    }
}

fn parse_int(int: syn::Lit, span: Span, field: &str) -> Result<usize, syn::Error> {
    match int {

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Switch the flavor to `current_thread`: `#[dbt_runtime::main(flavor = "current_thread", unhandled_panic = "abort_runtime")]`.
  2. Remove the `unhandled_panic` option if the multi_thread runtime is required.
  3. If panic propagation is the goal on multi_thread, install a panic hook or join spawned tasks explicitly to detect panics instead of using unhandled_panic.

Example fix

// before
#[dbt_runtime::main(flavor = "multi_thread", unhandled_panic = "abort_runtime")]
async fn main() { /* ... */ }

// after
#[dbt_runtime::main(flavor = "current_thread", unhandled_panic = "abort_runtime")]
async fn main() { /* ... */ }
Defensive patterns

Strategy: validation

Validate before calling

// unhandled_panic is only supported on current_thread/local flavors.
const FLAVOR: &str = "multi_thread";
const UNHANDLED_PANIC: Option<&str> = Some("abort_runtime");
const _: () = assert!(UNHANDLED_PANIC.is_none() || FLAVOR != "multi_thread", "unhandled_panic requires flavor = \"current_thread\"");

Type guard

fn is_unhandled_panic_compatible(flavor: &str) -> bool {
    matches!(flavor, "current_thread" | "local")
}

Prevention

When it happens

Trigger: Writing `#[dbt_runtime::main(flavor = "multi_thread", unhandled_panic = "abort_runtime")]` (or the equivalent `#[dbt_runtime::test(...)]`), or omitting the flavor (default multi_thread) while passing `unhandled_panic`. Raised in `build` when flavor == RuntimeFlavor::Threaded and self.unhandled_panic is Some.

Common situations: Adding `unhandled_panic = "abort_runtime"` to harden a multi-threaded service entry point; copying tokio attribute configuration between runtimes; tests migrated from single-thread tokio tests to multi_thread without auditing options.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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