risingwavelabs/risingwave · error
expect PbNodeBody::StreamScan but got: {:?}
Error message
expect PbNodeBody::StreamScan but got: {:?} What it means
After resolving the stream scan node (directly or via a Project indirection), the code destructures its body as `PbNodeBody::StreamScan`. If the node is not a StreamScan, this error is returned. This is an internal invariant: the previous match should guarantee the node is a StreamScan, so hitting this indicates a logic bug or unexpected graph mutation.
Source
Thrown at src/meta/src/stream/stream_graph/fragment.rs:436
let [stream_scan_node] = stream_input_node.input.as_slice() else {
return Err(anyhow!(
"Project node must have exactly 1 input for auto schema change, but got {:?}",
stream_input_node.input.len()
)
.into());
};
stream_scan_node
}
_ => {
return Err(anyhow!(
"expect PbNodeBody::StreamScan or PbNodeBody::Project but got: {:?}",
stream_input_node.node_body
)
.into());
}
};
let PbNodeBody::StreamScan(scan) = stream_scan_node.node_body.as_ref().unwrap() else {
return Err(anyhow!(
"expect PbNodeBody::StreamScan but got: {:?}",
stream_scan_node.node_body
)
.into());
};
let stream_scan_type = PbStreamScanType::try_from(scan.stream_scan_type).unwrap();
if stream_scan_type != PbStreamScanType::ArrangementBackfill {
return Err(anyhow!(
"unsupported stream_scan_type for auto refresh schema: {:?}",
stream_scan_type
)
.into());
}
let [merge_node, _batch_plan_node] = stream_scan_node.input.as_slice() else {
panic!(
"the number of StreamScan inputs is not 2: {:?}",
stream_scan_node.input
);View on GitHub (pinned to 6469eb736d)
Solutions
- Restart the meta node to clear in-memory fragment state and retry the streaming job.
- Verify all components run the same RisingWave version.
- Capture the fragment graph dump from meta logs and inspect the node body.
- If reproducible, file a RisingWave bug — this is an internal invariant breach.
Example fix
// before
let PbNodeBody::StreamScan(scan) = stream_scan_node.node_body.as_ref().unwrap() else { ... };
// after: use the match result directly to avoid re-unwrapping
let PbNodeBody::StreamScan(scan) = node_body else {
unreachable!("match above guarantees StreamScan")
}; Defensive patterns
Strategy: type-guard
Validate before calling
if !matches!(stream_scan_node.node_body.as_ref(), Some(PbNodeBody::StreamScan(_))) {
panic!("invariant breached: scan node is {:?}", stream_scan_node.node_body);
} Type guard
fn as_stream_scan(node: &StreamNode) -> Option<&PbStreamScan> {
match node.node_body.as_ref() {
Some(PbNodeBody::StreamScan(scan)) => Some(scan),
_ => None,
}
} Try / catch
match result {
Ok(()) => {},
Err(e) if e.to_string().contains("expect PbNodeBody::StreamScan") => {
// internal invariant issue: restart meta / report bug
}
Err(e) => return Err(e),
} Prevention
- Do not mutate fragment nodes between validation and use.
- Cover graph-shape invariants with unit tests on generated graphs.
- Keep check and rewrite passes in the same module to avoid drift.
When it happens
Trigger: `generate_streaming_job` -> `check_sink_fragments_support_refresh_schema` where `stream_scan_node.node_body` is not `PbNodeBody::StreamScan` despite earlier matching — effectively unreachable except via inconsistent fragment data.
Common situations: Corrupted or concurrently modified fragment metadata in meta's memory store; non-deterministic node rewriting between validation passes; version drift between components.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- expect PbNodeBody::Sink but got: {:?}
- Project node must have exactly 1 input for auto schema chang
- expect PbNodeBody::Merge but got: {:?}
- expect PbNodeBody::Project but got: {:?}
- empty meta addresses
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/e8bba59e194fe0d8.
Report an issue: GitHub.