{"record":{"id":"50482ede5dd68743","repo":"risingwavelabs/risingwave","slug":"no-actors-to-assign-assignment-is-meaningless","errorCode":null,"errorMessage":"no actors to assign; assignment is meaningless","messagePattern":"no actors to assign; assignment is meaningless","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/meta/src/stream/stream_graph/assignment.rs","lineNumber":753,"sourceCode":"    }\n\n    /// Hierarchical assignment: Actors → Workers → `VNodes` → Actors.\n    pub fn assign_hierarchical<W, A, V>(\n        &self,\n        workers: &BTreeMap<W, NonZeroUsize>,\n        actors: &[A],\n        vnodes: &[V],\n    ) -> Result<BTreeMap<W, BTreeMap<A, Vec<V>>>>\n    where\n        W: Ord + Hash + Eq + Clone + Debug,\n        A: Ord + Hash + Eq + Copy + Debug,\n        V: Hash + Eq + Copy + Debug,\n    {\n        ensure!(\n            !workers.is_empty(),\n            \"no workers to assign; assignment is meaningless\"\n        );\n        ensure!(\n            !actors.is_empty(),\n            \"no actors to assign; assignment is meaningless\"\n        );\n        ensure!(\n            !vnodes.is_empty(),\n            \"no vnodes to assign; assignment is meaningless\"\n        );\n        ensure!(\n            vnodes.len() >= actors.len(),\n            \"not enough vnodes ({}) for actors ({}); each actor needs at least one vnode\",\n            vnodes.len(),\n            actors.len()\n        );\n\n        let chunk_size = match self.vnode_chunking_strategy {\n            VnodeChunkingStrategy::NoChunking => {\n                return assign_hierarchical(\n                    workers,","sourceCodeStart":735,"sourceCodeEnd":771,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/meta/src/stream/stream_graph/assignment.rs#L735-L771","documentation":"Same guard family as the worker check: `ensure!` in the inner `assign_hierarchical` (assignment.rs:753) refuses to run when the actor list is empty, because a scheduling decision over zero actors has no meaning.","triggerScenarios":"Calling the inner assignment routine with `actors.is_empty()`; mirrors the public entry's earlier check but in the ensure! chain.","commonSituations":"Fragments that produced no actors due to plan generation bugs; direct API/test usage with empty actor vectors.","solutions":["Ensure actors are generated (parallelism >= 1) for every fragment before assignment.","Add an earlier caller-side check with a fragment-id-bearing error message.","Investigate plan generation if a valid fragment ends up with zero actors."],"exampleFix":"// before\nlet plan = assign_hierarchical(&workers, &[], &vnodes, strategy)?;\n// after\nif actors.is_empty() {\n    return Err(anyhow!(\"fragment {} generated no actors\", fragment_id));\n}\nlet plan = assign_hierarchical(&workers, &actors, &vnodes, strategy)?;","handlingStrategy":"validation","validationCode":"// Rust, before scheduling\nif actors.is_empty() {\n    anyhow::bail!(\"fragment produced no actors\");\n}","typeGuard":"fn non_empty_actors<'a>(a: &'a [ActorId]) -> Option<&'a [ActorId]> {\n    (!a.is_empty()).then_some(a)\n}","tryCatchPattern":"match assign_hierarchical(&workers, &actors, &vnodes, strategy) {\n    Ok(m) => m,\n    Err(e) if e.to_string().contains(\"no actors\") => {\n        Err(anyhow!(\"plan generation bug: zero actors: {e:#}\"))\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Assert non-empty actor sets in planner/fragment tests.","Treat zero-actor fragments as plan-generation bugs, not runtime conditions.","Include fragment id in caller-side errors for faster triage."],"tags":["assignment","meta","empty-input","validation"],"backgroundTag":"empty-required-field","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}