tokio-rs/tokio · error · syn::Error

`worker_threads` may not be 0.

Error message

`worker_threads` may not be 0.

What it means

`set_worker_threads` (entry.rs:138) parses the value as `usize` and explicitly forbids zero, because a multi-threaded runtime needs at least one worker thread. Zero is nonsensical and would otherwise fail later at runtime builder construction; the macro fails early instead.

Source

Thrown at tokio-macros/src/entry.rs:140

        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."));
        }

        let start_paused = parse_bool(start_paused, span, "start_paused")?;
        self.start_paused = Some((start_paused, span));
        Ok(())
    }

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

View on GitHub (pinned to 625954f365)

Solutions

  1. Set `worker_threads` to at least 1 (commonly `std::thread::available_parallelism()`).
  2. If deriving from configuration, clamp: `max(1, configured_value)`.
  3. Omit the option to let tokio pick the default (number of CPU cores).

Example fix

// before
#[tokio::main(worker_threads = 0)]
async fn main() {}

// after — let tokio choose, or set a positive count
#[tokio::main(flavor = "multi_thread", worker_threads = 4)]
async fn main() {}
Defensive patterns

Strategy: validation

Validate before calling

// If deriving worker count from config, clamp before passing to a manual builder:
let n = config.worker_threads.unwrap_or_else(|| {
    std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1)
});
let n = std::cmp::max(1, n);
// Note: the macro requires a literal, so use this only with the runtime builder API.

Prevention

When it happens

Trigger: Writing `#[tokio::main(worker_threads = 0)]`, or passing a computed value that resolves to 0.

Common situations: Driving `worker_threads` from an environment variable / config without clamping to a minimum of 1; miscounting available parallelism and subtracting one too many.

Related errors


AI-assisted analysis of tokio-rs/tokio@625954f365 (2026-08-11). Data as JSON: /api/errors/f6fb162332656d24. Report an issue: GitHub.