risingwavelabs/risingwave · error
Expected RexNode::FuncCall
Error message
Expected RexNode::FuncCall
What it means
get_children_and_return_type expects every prost ExprNode it receives to contain a RexNode::FuncCall, from which it extracts child nodes and return type. If the rex_node is any other variant (e.g. InputRef, Literal, or unspecified), it bails with this message. This is an internal invariant check during expression tree building.
Solutions
- Check component versions are consistent (frontend vs compute nodes) and redeploy uniformly
- Inspect the plan protobuf to find the node with the wrong rex_node type
- Verify the plan was produced by a matching frontend version, not hand-edited
- Extend get_children_and_return_type to handle new RexNode variants if a new node kind is intentional
Defensive patterns
Strategy: type-guard
Validate before calling
// Before deserializing/executing a plan, check node kinds assert!(node.get_rex_node().map(|r| matches!(r, RexNode::FuncCall(_))).unwrap_or(false));
Type guard
fn is_func_call(n: &ExprNode) -> bool { matches!(n.get_rex_node(), Some(RexNode::FuncCall(_))) } Try / catch
if let Err(e) = build(node) { if e.to_string().contains("Expected RexNode::FuncCall") { report_plan_corruption(&node); } return Err(e); } Prevention
- Deploy frontend and compute nodes from the same release
- Never hand-edit plan protobufs
- Regenerate plans after upgrading component versions
When it happens
Trigger: Calling build (or anything reaching get_children_and_return_type) with an ExprNode whose rex_node field is not FuncCall — typically malformed protobuf input from an upstream planner or a hand-crafted/deployed plan mismatch.
Common situations: Version-skewed components where frontend emits new node kinds an older expr builder doesn't recognize; corrupted or manually edited query plans; serialization bugs.
Related errors
- BatchPosixFsReader should not be used
- BatchPosixFsReader should not hit this branch. refer to…
- builder should have yielded a chunk
- Can't create deltalake sink write result from empty data!
- Can't find
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/57d33feb3750ef3d.
Report an issue: GitHub.
Appendix: source
Thrown at src/expr/core/src/expr/build.rs:266
func: PbType,
ret_type: DataType,
children: Vec<BoxedExpression>,
error_report: impl EvalErrorReport + 'static,
) -> Result<NonStrictExpression> {
let expr = build_func(func, ret_type, children)?;
let wrapped = ExprBuilder::new_non_strict(error_report).wrap(expr);
Ok(match wrapped {
BoxedExpression::Sync(expr) => NonStrictExpression::Sync(expr),
BoxedExpression::Async(expr) => NonStrictExpression::Async(expr),
})
}
pub(super) fn get_children_and_return_type(prost: &ExprNode) -> Result<(&[ExprNode], DataType)> {
let ret_type = DataType::from(prost.get_return_type().unwrap());
if let RexNode::FuncCall(func_call) = prost.get_rex_node().unwrap() {
Ok((func_call.get_children(), ret_type))
} else {
bail!("Expected RexNode::FuncCall");
}
}
/// Build an expression from a string.
///
/// # Example
///
/// ```ignore
/// # use risingwave_expr::expr::build_from_pretty;
/// build_from_pretty("42:int2"); // literal
/// build_from_pretty("$0:int8"); // inputref
/// build_from_pretty("(add:int8 42:int2 $1:int8)"); // function
/// build_from_pretty("(add:int8 42:int2 (add:int8 42:int2 $1:int8))");
/// ```
///
/// # Syntax
///
/// ```textView on GitHub (pinned to 6469eb736d)