risingwavelabs/risingwave · error

no actors to assign

Error message

no actors to assign

What it means

The generic `assign_hierarchical` worker/actor/vnode assignment function requires at least one actor to distribute; an empty actor list makes the assignment meaningless. It fails fast at the top of the function before any distribution work.

Source

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

///    - For each worker, take its vnode list and assign them to actors in simple round-robin:
///      iterate vnodes in order, dispatching index `% actor_list.len()`.
///    - Collect into final `BTreeMap<W, BTreeMap<A, Vec<V>>>`.
pub fn assign_hierarchical<W, A, V, S>(
    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,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure the fragment graph has generated actors (parallelism >= 1) before calling assignment.
  2. Guard the caller: skip assignment or return a domain error when `actors.is_empty()`.
  3. Check why upstream rewrite produced zero actors for the fragment (e.g. disabled fragments) and fix plan generation.

Example fix

// before
let mapping = assign_hierarchical(&workers, &[], &vnodes, strategy)?;
// after
if actors.is_empty() {
    anyhow::bail!("fragment has no actors to assign");
}
let mapping = assign_hierarchical(&workers, &actors, &vnodes, strategy)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust, before calling assignment
if actors.is_empty() {
    anyhow::bail!("refusing to assign: actor list is empty");
}

Type guard

fn non_empty<'a, T>(xs: &'a [T]) -> Option<&'a [T]> {
    if xs.is_empty() { None } else { Some(xs) }
}

Try / catch

match assign_hierarchical(&workers, &actors, &vnodes, strategy) {
    Ok(mapping) => mapping,
    Err(e) if e.to_string().contains("no actors to assign") => {
        tracing::warn!("skipping assignment: no actors generated");
        BTreeMap::new()
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `assign_hierarchical` (assignment.rs:397) with `actors.is_empty() == true`; tests `error_on_empty_actors` exercise this directly.

Common situations: Building/assigning a stream fragment whose actor set was not generated (e.g. a degenerate fragment with zero parallelism); calling the assignment API in tests or tools with an empty actor vector; upstream bug that filtered out all actors before assignment.

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


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