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

`worker_threads` set multiple times.

Error message

`worker_threads` set multiple times.

What it means

Each #[tokio::main]/#[tokio::test] attribute may specify `worker_threads` at most once. The generated BuildConfig stores Option<worker_threads>; if set_flavor-style setter sees it is already Some, it returns a syn::Error pointing at the duplicate key. This guards against ambiguous configuration rather than silently picking one value.

Source

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

    fn set_flavor(&mut self, runtime: syn::Lit, span: Span) -> Result<(), syn::Error> {
        if self.flavor.is_some() {
            return Err(syn::Error::new(span, "`flavor` set multiple times."));
        }

        let runtime_str = parse_string(runtime, span, "flavor")?;
        let runtime =
            RuntimeFlavor::from_str(&runtime_str).map_err(|err| syn::Error::new(span, err))?;
        self.flavor = Some(runtime);
        Ok(())
    }

    fn set_worker_threads(
        &mut self,
        worker_threads: syn::Lit,
        span: Span,
    ) -> Result<(), syn::Error> {
        if self.worker_threads.is_some() {
            return Err(syn::Error::new(
                span,
                "`worker_threads` set multiple times.",
            ));
        }

        let worker_threads = parse_int(worker_threads, span, "worker_threads")?;
        if worker_threads == 0 {
            return Err(syn::Error::new(span, "`worker_threads` may not be 0."));
        }
        self.worker_threads = Some((worker_threads, span));
        Ok(())
    }

    fn set_start_paused(&mut self, start_paused: syn::Lit, span: Span) -> Result<(), syn::Error> {
        if self.start_paused.is_some() {
            return Err(syn::Error::new(span, "`start_paused` set multiple times."));
        }

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Remove the duplicate `worker_threads = N` key, keeping only one
  2. If you need runtime-configurable thread counts, drop the attribute and build the runtime manually with tokio::runtime::Builder
  3. Combine the two values into one choice before invoking the macro

Example fix

// before
#[tokio::main(flavor = "multi_thread", worker_threads = 4, worker_threads = 8)]
async fn main() {}

// after
#[tokio::main(flavor = "multi_thread", worker_threads = 8)]
async fn main() {}
Defensive patterns

Strategy: validation

Validate before calling

fn check_no_dupes(attr_src: &str, key: &str) -> Result<(), String> {
    let n = attr_src.split(',').filter(|p| p.trim().starts_with(key)).count();
    if n > 1 { Err(format!("`{key}` specified {n} times")) } else { Ok(()) }
}

Prevention

When it happens

Trigger: Writing #[tokio::main(flavor = "multi_thread", worker_threads = 4, worker_threads = 8)] — the same attribute key appears twice in the token stream passed to build_config.

Common situations: Merging two attribute lines by hand; a macro or code generator emitting the option twice; copy-paste of an attribute line leaving the old worker_threads behind.

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/2d644121779d8c59. Report an issue: GitHub.