risingwavelabs/risingwave · error

Expect at least 1 CDC table snapshot splits, 0 was found.

Error message

Expect at least 1 CDC table snapshot splits, 0 was found.

What it means

The mirror-side guard of assign_cdc_table_snapshot_splits: it rejects when the snapshot split list is empty, since there is nothing to distribute to the actors. Raised when reassign_splits reloads splits for a CDC table and finds zero pending snapshot splits despite actors being available.

Solutions

  1. Verify the CDC table's snapshot backfill state; if already complete, no snapshot reassignment is needed — the error is a benign race and can be retried/ignored.
  2. Check the split rows for the job in the meta store; if splits were removed unexpectedly, restore from backup or recreate the CDC table.
  3. Retry the operation; the scheduler should skip snapshot reassignment once backfill is finished.
  4. If persistent with pending splits missing, recreate the CDC table's backfill.
Defensive patterns

Strategy: validation

Validate before calling

let pending: Vec<_> = load_splits_for_job(job_id).await?;
let snapshot_splits: Vec<_> = pending.into_iter().filter(|s| s.is_snapshot()).collect();
if !snapshot_splits.is_empty() {
    assign_cdc_table_snapshot_splits(actors, &snapshot_splits, generation)?;
}

Try / catch

match reassign_splits(job_id).await {
    Err(e) if e.to_string().contains("Expect at least 1 CDC table snapshot splits") => {
        // snapshot backfill likely already completed; log and continue
        tracing::debug!(%job_id, "no snapshot splits left to assign");
    }
    other => other?,
}

Prevention

When it happens

Trigger: reassign_splits calls assign_cdc_table_snapshot_splits with a non-empty actor set but an empty slices of CdcTableSnapshotSplitRaw — e.g. all snapshot splits were already assigned/completed but a reschedule still requested snapshot reassignment, or the meta store returned no splits for the table.

Common situations: Backfill already finished when a recovery/scale event triggers reassignment; concurrent completion of splits by another meta operation; race between backfill completion and reschedule.

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/6816a6d4638b5e80. Report an issue: GitHub.

Appendix: source

Thrown at src/meta/src/stream/cdc.rs:199

    assert_eq!(
        stream_scan_fragments.count(),
        0,
        "Expect no remaining scan fragment",
    );
    Some(fragment)
}

pub(crate) fn assign_cdc_table_snapshot_splits(
    actor_ids: HashSet<ActorId>,
    splits: &[CdcTableSnapshotSplitRaw],
    generation: u64,
) -> MetaResult<HashMap<ActorId, PbCdcTableSnapshotSplits>> {
    if actor_ids.is_empty() {
        return Err(anyhow::anyhow!("Expect at least 1 actor, 0 was found.").into());
    }
    if splits.is_empty() {
        return Err(
            anyhow::anyhow!("Expect at least 1 CDC table snapshot splits, 0 was found.").into(),
        );
    }
    let splits_per_actor = splits.len().div_ceil(actor_ids.len());
    let mut assignments = HashMap::new();
    for (actor_id, splits) in actor_ids.iter().copied().zip_eq_debug(
        splits
            .iter()
            .map(build_cdc_table_snapshot_split)
            .chunks(splits_per_actor)
            .into_iter()
            .map(|c| c.collect_vec())
            .chain(iter::repeat(Vec::default()))
            .take(actor_ids.len()),
    ) {
        assignments.insert(actor_id, PbCdcTableSnapshotSplits { splits, generation });
    }
    Ok(assignments)
}

View on GitHub (pinned to 6469eb736d)