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

The default runtime flavor is `multi_thread`, but the `rt-mu

Error message

The default runtime flavor is `multi_thread`, but the `rt-multi-thread` feature is disabled.

What it means

When no flavor is specified the macro assumes the default multi_thread runtime, but that requires the `rt-multi-thread` cargo feature. If the feature is disabled and flavor was never set, build() emits this compile error at the call site via Span::call_site(). The macro cannot silently downgrade to current_thread because the generated runtime semantics would differ.

Source

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

        let worker_threads = match (flavor, self.worker_threads) {
            (F::CurrentThread | F::Local, Some((_, worker_threads_span))) => {
                let msg = format!(
                    "The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[{}(flavor = \"multi_thread\")]`",
                    self.macro_name(),
                );
                return Err(syn::Error::new(worker_threads_span, msg));
            }
            (F::CurrentThread | F::Local, None) => None,
            (F::Threaded, worker_threads) if self.rt_multi_thread_available => {
                worker_threads.map(|(val, _span)| val)
            }
            (F::Threaded, _) => {
                let msg = if self.flavor.is_none() {
                    "The default runtime flavor is `multi_thread`, but the `rt-multi-thread` feature is disabled."
                } else {
                    "The runtime flavor `multi_thread` requires the `rt-multi-thread` feature."
                };
                return Err(syn::Error::new(Span::call_site(), msg));
            }
        };

        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!(

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Enable the feature: tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
  2. Explicitly opt into the single-thread runtime: #[tokio::main(flavor = "current_thread")]
  3. Re-enable default features if they were disabled unintentionally

Example fix

// before (Cargo.toml)
tokio = { version = "1", features = ["rt", "macros"] }

// after
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
Defensive patterns

Strategy: validation

Validate before calling

// Check Cargo.toml before using bare #[tokio::main]
fn needs_rt_multi_thread(features: &[&str]) -> bool {
    !features.contains(&"rt-multi-thread")
}
// if this returns true, either add the feature or use flavor = "current_thread"

Prevention

When it happens

Trigger: Using #[tokio::main] or #[tokio::test] (no flavor argument) in a crate whose tokio dependency lacks the `rt-multi-thread` feature — e.g. tokio = { version = "1", features = ["rt"] } or a min-features build.

Common situations: Minimal-features/CI matrix builds; disabling default features in Cargo.toml; a dependency turning off tokio features via feature unification in a workspace with -Z avoid-dev-deps style setups; upgrading tokio and dropping default features.

Related errors


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