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

  1. Check component versions are consistent (frontend vs compute nodes) and redeploy uniformly
  2. Inspect the plan protobuf to find the node with the wrong rex_node type
  3. Verify the plan was produced by a matching frontend version, not hand-edited
  4. 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

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


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
///
/// ```text

View on GitHub (pinned to 6469eb736d)