{"record":{"id":"fded608e63f561b7","repo":"tokio-rs/tokio","slug":"there-must-be-more-than-one-worker","errorCode":null,"errorMessage":"There must be more than one worker","messagePattern":"There must be more than one worker","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tokio-util/src/task/spawn_pinned.rs","lineNumber":320,"sourceCode":"                }\n            }\n        })\n    }\n\n    /// Find the worker with the least number of tasks, increment its task\n    /// count, and return its handle. Make sure to actually spawn a task on\n    /// the worker so the task count is kept consistent with load.\n    ///\n    /// A job count guard is also returned to ensure the task count gets\n    /// decremented when the job is done.\n    fn find_and_incr_least_burdened_worker(&self) -> (&LocalWorkerHandle, JobCountGuard) {\n        loop {\n            let (worker, task_count) = self\n                .workers\n                .iter()\n                .map(|worker| (worker, worker.task_count.load(Ordering::SeqCst)))\n                .min_by_key(|&(_, count)| count)\n                .expect(\"There must be more than one worker\");\n\n            // Make sure the task count hasn't changed since when we choose this\n            // worker. Otherwise, restart the search.\n            if worker\n                .task_count\n                .compare_exchange(\n                    task_count,\n                    task_count + 1,\n                    Ordering::SeqCst,\n                    Ordering::Relaxed,\n                )\n                .is_ok()\n            {\n                return (worker, JobCountGuard(Arc::clone(&worker.task_count)));\n            }\n        }\n    }\n","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/tokio-rs/tokio/blob/625954f365727668cb02d04172b34f1149637728/tokio-util/src/task/spawn_pinned.rs#L302-L338","documentation":"spawn_pinned::find_and_incr_least_burdened_worker calls .expect('There must be more than one worker') on the iterator's min_by_key. It panics if self.workers is empty — i.e. spawn_pinned was used on a LocalPoolSet that has zero workers.","triggerScenarios":"Calling spawn_pinned on a LocalPoolSet built with zero worker threads; calling after all workers were removed; constructing the pool with an empty worker list.","commonSituations":"Misconfiguration when building the spawn_pinned pool (worker count 0); shutdown that emptied the pool before a late spawn; misuse of internal APIs that allow empty pools.","solutions":["Construct the LocalPoolSet with at least one worker thread.","Guard against spawning after the pool has been shut down/emptied.","Add an assertion at pool construction time rejecting worker_count == 0.","Keep the pool alive for the duration of all spawn_pinned calls."],"exampleFix":"// before\nlet pool = LocalPoolHandle::new(0); // zero workers\npool.spawn_pinned(|| async {}); // panics\n// after\nlet pool = LocalPoolHandle::new(2);\npool.spawn_pinned(|| async {});","handlingStrategy":"validation","validationCode":"assert!(worker_count >= 1, \"LocalPoolSet needs >= 1 worker\");\nlet pool = tokio_util::task::LocalPoolHandle::new(worker_count);","typeGuard":"fn valid_pool_size(n: usize) -> bool { n >= 1 }","tryCatchPattern":"// Panic is internal; validate before constructing:\nif worker_count == 0 {\n    return Err(io::Error::new(io::ErrorKind::InvalidInput, \"need >= 1 worker\"));\n}\nlet pool = LocalPoolHandle::new(worker_count);","preventionTips":["Construct LocalPoolHandle with at least one worker.","Keep the pool alive across all spawn_pinned calls.","Don't spawn after pool shutdown."],"tags":["spawn-pinned","task","panic","configuration","tokio-util"],"backgroundTag":null,"analyzedSha":"625954f365727668cb02d04172b34f1149637728","analyzedAt":"2026-08-11T17:46:45.378Z","contentChangedAt":"2026-08-11T17:46:45.378Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}