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
- Ensure fit_snapshot_backfill_epochs runs (with correct operator ids) before collecting the info.
- Verify the upstream snapshot-backfill scan's epoch was set at creation; recreate the MV if it was created without one.
- 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
- Call fit_snapshot_backfill_epochs before collecting info.
- In tests, always assign snapshot_backfill_epoch when constructing SnapshotBackfill scans.
- Check operator-id matching between fit and collect phases.
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
- must be either all snapshot_backfill or no snapshot_backfill
- upstream table id not covered: {}
- auto schema change with drop column only supports Project wi
- new column id not found in scan output
- Different number of internal tables. New: {}, Old: {}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/f111e03ce5f39f93.
Report an issue: GitHub.