risingwavelabs/risingwave · error

upstream table id not set: {}

Error message

upstream table id not set: {}

What it means

Same lookup as 'upstream table id not covered', but here the table id exists in the map while its Option<u64> epoch value is None — i.e. the source scan's snapshot_backfill_epoch was never assigned when the info was collected. The function refuses to propagate a None epoch into the downstream scan.

Source

Thrown at src/meta/src/stream/stream_graph/fragment.rs:1623

    visit_stream_node_cont_mut(node, |node| {
        if let Some(NodeBody::StreamScan(stream_scan)) = node.node_body.as_mut()
            && (stream_scan.stream_scan_type == StreamScanType::SnapshotBackfill as i32
                || stream_scan.stream_scan_type == StreamScanType::CrossDbSnapshotBackfill as i32)
        {
            result = try {
                let table_id = stream_scan.table_id;
                let snapshot_epoch = cross_db_snapshot_backfill_info
                    .upstream_mv_table_id_to_backfill_epoch
                    .get(&table_id)
                    .or_else(|| {
                        snapshot_backfill_info.and_then(|snapshot_backfill_info| {
                            snapshot_backfill_info
                                .upstream_mv_table_id_to_backfill_epoch
                                .get(&table_id)
                        })
                    })
                    .ok_or_else(|| anyhow!("upstream table id not covered: {}", table_id))?
                    .ok_or_else(|| anyhow!("upstream table id not set: {}", table_id))?;
                if let Some(prev_snapshot_epoch) =
                    stream_scan.snapshot_backfill_epoch.replace(snapshot_epoch)
                {
                    Err(anyhow!(
                        "snapshot backfill epoch set again: {} {} {}",
                        table_id,
                        prev_snapshot_epoch,
                        snapshot_epoch
                    ))?;
                }
                applied = true;
            };
            result.is_ok()
        } else {
            true
        }
    });
    result.map_err(MetaError::from).map(|_| applied)

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure fit_snapshot_backfill_epochs runs (with correct operator ids) before collecting the info.
  2. Verify the upstream snapshot-backfill scan's epoch was set at creation; recreate the MV if it was created without one.
  3. If running tests, set snapshot_backfill_epoch explicitly on the StreamScan nodes before calling fill.
Defensive patterns

Strategy: validation

Validate before calling

// Ensure every snapshot-backfill scan has an epoch before collecting info:
// scan.snapshot_backfill_epoch.is_some() for all SnapshotBackfill scans.

Type guard

fn epoch_set(s: &StreamScanNode) -> bool { s.snapshot_backfill_epoch.is_some() }

Prevention

When it happens

Trigger: fill_snapshot_backfill_epoch encountering a scan whose upstream entry maps to None — the upstream StreamScan had snapshot_backfill_epoch = None during collect_snapshot_backfill_info_impl (epoch not yet fitted, e.g. in madsim/test paths or a missing fit_snapshot_backfill_epochs call).

Common situations: Graphs where fit_snapshot_backfill_epochs was skipped or operator ids didn't match, leaving epochs unset before collection; test tooling that builds snapshots without assigning backfill epochs.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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