risingwavelabs/risingwave · error
expect PbNodeBody::Project but got: {:?}
Error message
expect PbNodeBody::Project but got: {:?} What it means
When rewriting the Project node inserted for auto schema change (`rewrite_project_node`), the code requires the node's body to be `PbNodeBody::Project` so its expressions can be adjusted for added/removed columns. Any other variant returns this error, failing the schema refresh rewrite.
Source
Thrown at src/meta/src/stream/stream_graph/fragment.rs:685
merge.upstream_fragment_id = upstream_table_fragment_id;
Ok(ScanRewriteResult {
old_output_index_to_new_output_index,
new_output_index_by_column_id,
output_fields: stream_scan_node.fields.clone(),
})
}
/// Rewrite Project node input refs and extend with newly added columns.
fn rewrite_project_node(
project_node: &mut StreamNode,
scan_rewrite: &ScanRewriteResult,
newly_added_columns: &[ColumnCatalog],
removed_column_ids: &HashSet<ColumnId>,
upstream_table_name: &str,
) -> MetaResult<()> {
let PbNodeBody::Project(project_node_body) = project_node.node_body.as_mut().unwrap() else {
return Err(anyhow!(
"expect PbNodeBody::Project but got: {:?}",
project_node.node_body
)
.into());
};
let has_non_input_ref = project_node_body
.select_list
.iter()
.any(|expr| !matches!(expr.rex_node, Some(expr_node::RexNode::InputRef(_))));
if has_non_input_ref && !removed_column_ids.is_empty() {
return Err(anyhow!(
"auto schema change with drop column only supports Project with InputRef"
)
.into());
}
let mut new_select_list = Vec::with_capacity(project_node_body.select_list.len());
let mut new_project_fields = Vec::with_capacity(project_node.fields.len());View on GitHub (pinned to 6469eb736d)
Solutions
- Restart meta and retry the schema-changing operation to rebuild consistent graph state.
- Verify all RisingWave components are on the same version.
- Drop and recreate the sink to regenerate a canonical graph with a proper Project node.
- File a RisingWave bug with fragment graph dumps — the check pass should have matched this node as Project.
Example fix
// before
let PbNodeBody::Project(project_node_body) = project_node.node_body.as_mut().unwrap() else { ... };
// after: skip projection rewrite when shape is unexpected
let Some(PbNodeBody::Project(project_node_body)) = project_node.node_body.as_mut() else {
tracing::warn!("expected Project node for schema refresh, got {:?}; skipping", project_node.node_body);
return Ok(());
}; Defensive patterns
Strategy: type-guard
Validate before calling
if !matches!(project_node.node_body.as_ref(), Some(PbNodeBody::Project(_))) {
return Err("schema refresh expects a Project node here".into());
} Type guard
fn as_project_mut(node: &mut StreamNode) -> Option<&mut PbProjectNode> {
match node.node_body.as_mut() {
Some(PbNodeBody::Project(p)) => Some(p),
_ => None,
}
} Try / catch
match rewrite_project_node(&mut project_node, ...).await {
Ok(()) => {},
Err(e) if e.to_string().contains("expect PbNodeBody::Project") => {
// invariant breach: rebuild the sink graph
rebuild_sink_graph()?;
}
Err(e) => return Err(e),
} Prevention
- Ensure the node resolved as 'the project node' is actually a Project before rewriting.
- Keep check-pass and rewrite-pass shape assumptions in shared helper functions.
- Exercise ALTER TABLE ADD/DROP COLUMN against sinks in staging regularly.
When it happens
Trigger: `rewrite_refresh_schema_sink_fragment` -> `rewrite_project_node` called with a node whose `node_body` is not `PbNodeBody::Project` (e.g. it was resolved via a Project branch but the body is actually something else).
Common situations: Upstream table schema change (ALTER TABLE ADD/DROP COLUMN, schema registry evolution) triggering a rewrite against inconsistent fragment metadata; frontend/meta version drift; internal invariant breach between the check and rewrite passes.
Related errors
- Project node must have exactly 1 input for auto schema chang
- expect PbNodeBody::StreamScan or PbNodeBody::Project but got
- expect PbNodeBody::StreamScan but got: {:?}
- expect PbNodeBody::Sink but got: {:?}
- unsupported stream_scan_type for auto refresh schema: {:?}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/9e1f88838168310d.
Report an issue: GitHub.