{"record":{"id":"6a7318cb6022d7e2","repo":"risingwavelabs/risingwave","slug":"valuesexecutor-should-have-no-child","errorCode":null,"errorMessage":"ValuesExecutor should have no child!","messagePattern":"ValuesExecutor should have no child!","errorType":"validation","errorClass":"BatchError","httpStatus":null,"severity":"error","filePath":"src/batch/executors/src/executor/values.rs","lineNumber":109,"sourceCode":"                let columns: Vec<_> = array_builders\n                    .into_iter()\n                    .map(|b| b.finish().into())\n                    .collect();\n\n                let chunk = DataChunk::new(columns, chunk_size);\n\n                yield chunk\n            }\n        }\n    }\n}\n\nimpl BoxedExecutorBuilder for ValuesExecutor {\n    async fn new_boxed_executor(\n        source: &ExecutorBuilder<'_>,\n        inputs: Vec<BoxedExecutor>,\n    ) -> Result<BoxedExecutor> {\n        ensure!(inputs.is_empty(), \"ValuesExecutor should have no child!\");\n        let value_node = try_match_expand!(\n            source.plan_node().get_node_body().unwrap(),\n            NodeBody::Values\n        )?;\n\n        let mut rows: Vec<Vec<BoxedExpression>> = Vec::with_capacity(value_node.get_tuples().len());\n        for row in value_node.get_tuples() {\n            let expr_row: Vec<_> = row.get_cells().iter().map(build_from_prost).try_collect()?;\n            rows.push(expr_row);\n        }\n\n        let fields = value_node\n            .get_fields()\n            .iter()\n            .map(Field::from)\n            .collect::<Vec<Field>>();\n\n        Ok(Box::new(Self {","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/batch/executors/src/executor/values.rs#L91-L127","documentation":"ValuesExecutor's builder requires zero child executors. A VALUES clause generates rows from literal expression tuples only and is always a leaf in the batch plan, so receiving an input executor means the plan tree was constructed incorrectly.","triggerScenarios":"new_boxed_executor receives a non-empty `inputs` vector because a Values plan node has children attached.","commonSituations":"Planner bugs attaching a child under a Values node (e.g. when wrapping VALUES in a project/limit and misplacing it in the tree); hand-built plans in tests; internal refactors changing arity rules.","solutions":["Verify the plan tree so the Values node has no children; fix the frontend/batch planner accordingly.","Ensure VALUES row data is expressed via value_node.tuples expressions rather than child executors.","Pass an empty inputs vec when constructing ValuesExecutor directly in tests.","Report an internal bug with the query if the plan seems valid."],"exampleFix":"// before\nBatchPlanNode::new(NodeBody::Values(values_node), vec![child])\n// after\nBatchPlanNode::new(NodeBody::Values(values_node), vec![])","handlingStrategy":"try-catch","validationCode":"// Rust: VALUES must be a leaf plan node\nassert!(inputs.is_empty(), \"Values must have no children\");","typeGuard":"fn is_leaf(inputs: &[BoxedExecutor]) -> bool { inputs.is_empty() }","tryCatchPattern":"if let Err(e) = builder.new_boxed_executor(&src, inputs).await {\n    if e.to_string().contains(\"ValuesExecutor should have no child\") {\n        // dump plan and fix planner arity\n    }\n}","preventionTips":["Construct Values nodes with empty children lists.","Represent VALUES rows as expression tuples only.","Add arity assertions in planner tests."],"tags":["batch-executor","values-clause","planner","rust","internal-invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}