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

The `worker_threads` option requires the `multi_thread`…

Error message

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

What it means

The `worker_threads` option only makes sense for the multi_thread flavor; current_thread (and local) runtimes are single-threaded, so the macro's build() validation rejects the combination at compile time and points the error at the worker_threads value's span. The message suggests adding flavor = "multi_thread".

Solutions

  1. Remove the worker_threads option when using flavor = "current_thread" or "local"
  2. Change the flavor to "multi_thread" if you actually need multiple workers
  3. Set the rt-multi-thread feature so multi_thread flavor is available

Example fix

// before
#[tokio::test(flavor = "current_thread", worker_threads = 4)]
async fn t() {}

// after
#[tokio::test(flavor = "current_thread")]
async fn t() {}
Defensive patterns

Strategy: validation

Validate before calling

fn check_attr_compat(flavor: &str, worker_threads: Option<u64>) -> Result<(), String> {
    if flavor != "multi_thread" && worker_threads.is_some() {
        Err(format!("worker_threads requires flavor=\"multi_thread\" (got {flavor})"))
    } else { Ok(()) }
}

Prevention

When it happens

Trigger: #[tokio::main(flavor = "current_thread", worker_threads = 4)] or #[tokio::test(flavor = "current_thread", worker_threads = 2)] — any pairing of current_thread/local flavor with a Some(worker_threads).

Common situations: Switching a test from multi_thread to current_thread for determinism and forgetting to delete worker_threads; cargo feature changes making rt-multi-thread unavailable; copying config across attributes.

Related errors


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

Appendix: source

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

        if self.is_test {
            "dbt_runtime::test"
        } else {
            "dbt_runtime::main"
        }
    }

    fn build(&self) -> Result<FinalConfig, syn::Error> {
        use RuntimeFlavor as F;

        let flavor = self.flavor.unwrap_or(self.default_flavor);

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

View on GitHub (pinned to 0267ce9170)