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

  1. Restart the meta node to clear in-memory fragment state and retry the streaming job.
  2. Verify all components run the same RisingWave version.
  3. Capture the fragment graph dump from meta logs and inspect the node body.
  4. 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

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


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