risingwavelabs/risingwave · error · BatchError

ValuesExecutor should have no child!

Error message

ValuesExecutor should have no child!

What it means

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.

Source

Thrown at src/batch/executors/src/executor/values.rs:109

                let columns: Vec<_> = array_builders
                    .into_iter()
                    .map(|b| b.finish().into())
                    .collect();

                let chunk = DataChunk::new(columns, chunk_size);

                yield chunk
            }
        }
    }
}

impl BoxedExecutorBuilder for ValuesExecutor {
    async fn new_boxed_executor(
        source: &ExecutorBuilder<'_>,
        inputs: Vec<BoxedExecutor>,
    ) -> Result<BoxedExecutor> {
        ensure!(inputs.is_empty(), "ValuesExecutor should have no child!");
        let value_node = try_match_expand!(
            source.plan_node().get_node_body().unwrap(),
            NodeBody::Values
        )?;

        let mut rows: Vec<Vec<BoxedExpression>> = Vec::with_capacity(value_node.get_tuples().len());
        for row in value_node.get_tuples() {
            let expr_row: Vec<_> = row.get_cells().iter().map(build_from_prost).try_collect()?;
            rows.push(expr_row);
        }

        let fields = value_node
            .get_fields()
            .iter()
            .map(Field::from)
            .collect::<Vec<Field>>();

        Ok(Box::new(Self {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Verify the plan tree so the Values node has no children; fix the frontend/batch planner accordingly.
  2. Ensure VALUES row data is expressed via value_node.tuples expressions rather than child executors.
  3. Pass an empty inputs vec when constructing ValuesExecutor directly in tests.
  4. Report an internal bug with the query if the plan seems valid.

Example fix

// before
BatchPlanNode::new(NodeBody::Values(values_node), vec![child])
// after
BatchPlanNode::new(NodeBody::Values(values_node), vec![])
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: VALUES must be a leaf plan node
assert!(inputs.is_empty(), "Values must have no children");

Type guard

fn is_leaf(inputs: &[BoxedExecutor]) -> bool { inputs.is_empty() }

Try / catch

if let Err(e) = builder.new_boxed_executor(&src, inputs).await {
    if e.to_string().contains("ValuesExecutor should have no child") {
        // dump plan and fix planner arity
    }
}

Prevention

When it happens

Trigger: new_boxed_executor receives a non-empty `inputs` vector because a Values plan node has children attached.

Common situations: 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.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/6a7318cb6022d7e2. Report an issue: GitHub.