{"record":{"id":"57d33feb3750ef3d","repo":"risingwavelabs/risingwave","slug":"expected-rexnode-funccall","errorCode":null,"errorMessage":"Expected RexNode::FuncCall","messagePattern":"Expected RexNode::FuncCall","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/core/src/expr/build.rs","lineNumber":266,"sourceCode":"    func: PbType,\n    ret_type: DataType,\n    children: Vec<BoxedExpression>,\n    error_report: impl EvalErrorReport + 'static,\n) -> Result<NonStrictExpression> {\n    let expr = build_func(func, ret_type, children)?;\n    let wrapped = ExprBuilder::new_non_strict(error_report).wrap(expr);\n    Ok(match wrapped {\n        BoxedExpression::Sync(expr) => NonStrictExpression::Sync(expr),\n        BoxedExpression::Async(expr) => NonStrictExpression::Async(expr),\n    })\n}\n\npub(super) fn get_children_and_return_type(prost: &ExprNode) -> Result<(&[ExprNode], DataType)> {\n    let ret_type = DataType::from(prost.get_return_type().unwrap());\n    if let RexNode::FuncCall(func_call) = prost.get_rex_node().unwrap() {\n        Ok((func_call.get_children(), ret_type))\n    } else {\n        bail!(\"Expected RexNode::FuncCall\");\n    }\n}\n\n/// Build an expression from a string.\n///\n/// # Example\n///\n/// ```ignore\n/// # use risingwave_expr::expr::build_from_pretty;\n/// build_from_pretty(\"42:int2\"); // literal\n/// build_from_pretty(\"$0:int8\"); // inputref\n/// build_from_pretty(\"(add:int8 42:int2 $1:int8)\"); // function\n/// build_from_pretty(\"(add:int8 42:int2 (add:int8 42:int2 $1:int8))\");\n/// ```\n///\n/// # Syntax\n///\n/// ```text","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/core/src/expr/build.rs#L248-L284","documentation":"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.","triggerScenarios":"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.","commonSituations":"Version-skewed components where frontend emits new node kinds an older expr builder doesn't recognize; corrupted or manually edited query plans; serialization bugs.","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"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Before deserializing/executing a plan, check node kinds\nassert!(node.get_rex_node().map(|r| matches!(r, RexNode::FuncCall(_))).unwrap_or(false));","typeGuard":"fn is_func_call(n: &ExprNode) -> bool { matches!(n.get_rex_node(), Some(RexNode::FuncCall(_))) }","tryCatchPattern":"if let Err(e) = build(node) { if e.to_string().contains(\"Expected RexNode::FuncCall\") { report_plan_corruption(&node); } return Err(e); }","preventionTips":["Deploy frontend and compute nodes from the same release","Never hand-edit plan protobufs","Regenerate plans after upgrading component versions"],"tags":["rust","protobuf","expression-build","internal-invariant"],"backgroundTag":"unexpected-response-shape","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}