risingwavelabs/risingwave · error
no vnodes to assign
Error message
no vnodes to assign
What it means
`assign_hierarchical` requires a non-empty virtual node list to distribute actors over; an empty vnode list makes balanced assignment impossible. It is a fast-fail precondition at the top of the function.
Source
Thrown at src/meta/src/stream/stream_graph/assignment.rs:401
workers: &BTreeMap<W, NonZeroUsize>,
actors: &[A],
virtual_nodes: &[V],
salt: S,
actor_capacity_mode: CapacityMode,
balanced_by: BalancedBy,
) -> anyhow::Result<BTreeMap<W, BTreeMap<A, Vec<V>>>>
where
W: Ord + Hash + Eq + Clone + Debug,
A: Ord + Hash + Eq + Copy + Clone + Debug,
V: Hash + Eq + Copy + Clone + Debug,
S: Hash + Copy,
{
if actors.is_empty() {
return Err(anyhow!("no actors to assign"));
}
if virtual_nodes.is_empty() {
return Err(anyhow!("no vnodes to assign"));
}
// Validate input: ensure vnode count can cover all actors
if actors.len() > virtual_nodes.len() {
return Err(anyhow!(
"actor count ({}) exceeds vnode count ({})",
actors.len(),
virtual_nodes.len()
));
}
let actor_capacity_fn = match actor_capacity_mode {
CapacityMode::Weighted => weighted_scale,
CapacityMode::Unbounded => unbounded_scale,
};
// Distribute actors across workers based on their weight
let actor_to_worker: BTreeMap<W, Vec<A>> =View on GitHub (pinned to 6469eb736d)
Solutions
- Ensure vnode count is >= 1 for the fragment (default is 256 in RisingWave) before assignment.
- Guard the caller to reject fragments with empty vnode sets earlier with a clearer error.
- Fix any code that computes vnodes from `maybe_vnode_count` so zero/unknown counts are handled before assignment.
Example fix
// before let mapping = assign_hierarchical(&workers, &actors, &[], strategy)?; // after assert!(!vnodes.is_empty(), "vnodes must be generated before assignment"); let mapping = assign_hierarchical(&workers, &actors, &vnodes, strategy)?;
Defensive patterns
Strategy: validation
Validate before calling
// Rust, before calling assignment
if vnodes.is_empty() {
anyhow::bail!("refusing to assign: vnode list is empty");
} Type guard
fn has_vnodes(v: &[VnodeId]) -> bool { !v.is_empty() } Try / catch
match assign_hierarchical(&workers, &actors, &vnodes, strategy) {
Ok(m) => m,
Err(e) if e.to_string().contains("no vnodes to assign") => {
Err(anyhow!("vnode set missing; check vnode count config: {e:#}"))
}
Err(e) => Err(e),
} Prevention
- Use the default vnode count (256) unless there is a specific reason not to.
- Handle unset `maybe_vnode_count` before computing vnode sets.
- Add unit tests for zero-vnode-count table configs.
When it happens
Trigger: Calling `assign_hierarchical` (assignment.rs:401) with `virtual_nodes.is_empty() == true`; test `error_on_empty_vnodes` triggers it.
Common situations: Passing a vnode set computed from a zero vnode-count table; misconfigured parallelism/vnode-count of 0; calling the assignment helper directly in tests with empty vnode slices.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- no actors to assign
- no actors to assign; assignment is meaningless
- actor count ({}) exceeds vnode count ({})
- no workers to assign; assignment is meaningless
- no vnodes to assign; assignment is meaningless
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/45a97c9909194305.
Report an issue: GitHub.