risingwavelabs/risingwave · error

actor count ({}) exceeds vnode count ({})

Error message

actor count ({}) exceeds vnode count ({})

What it means

`assign_hierarchical` validates that the number of actors does not exceed the number of virtual nodes, since every actor needs at least one vnode. When `actors.len() > virtual_nodes.len()`, balanced per-actor assignment is impossible and the function returns this descriptive error.

Source

Thrown at src/meta/src/stream/stream_graph/assignment.rs:406

    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>> =
        assign_items_weighted_with_scale_fn(workers, actors, salt, actor_capacity_fn);

    // Build unit-weight map for active workers (those with assigned actors)
    let mut active_worker_weights: BTreeMap<W, NonZeroUsize> = BTreeMap::new();

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Increase the vnode count to at least the actor count (RisingWave default is 256).
  2. Reduce fragment parallelism so actors.len() <= vnodes.len().
  3. When calling the API directly, always derive vnode count from the same config as parallelism instead of hardcoding.

Example fix

// before
let vnodes: Vec<u32> = (0..2).collect();
let mapping = assign_hierarchical(&workers, &actors, &vnodes, strategy)?; // 4 actors
// after
let vnodes: Vec<u32> = (0..256).collect();
let mapping = assign_hierarchical(&workers, &actors, &vnodes, strategy)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust, before calling assignment
if actors.len() > vnodes.len() {
    anyhow::bail!("need at least {} vnodes, have {}", actors.len(), vnodes.len());
}

Try / catch

match assign_hierarchical(&workers, &actors, &vnodes, strategy) {
    Ok(m) => m,
    Err(e) if e.to_string().contains("exceeds vnode count") => {
        Err(anyhow!("increase vnode count or reduce parallelism: {e:#}"))
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Calling `assign_hierarchical` (assignment.rs:406) with more actor ids than vnode ids; test `error_when_more_actors_than_vnodes` exercises this.

Common situations: Manually constructing a tiny vnode set (e.g. 2 vnodes) while creating more actors (e.g. 4-way parallelism); test/tooling misuse of the generic API; a vnode-count override below the fragment's parallelism.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/d87ab14f55ba73fc. Report an issue: GitHub.