risingwavelabs/risingwave · error · BatchError

Row sequential scan should not have input executor!

Error message

Row sequential scan should not have input executor!

What it means

RowSeqScanExecutorBuilder::new_boxed_executor asserts that the row sequential scan executor is built with zero child executors. A row seq scan is a leaf node in the batch query plan that reads rows directly from a table, so the executor framework must hand it no inputs. Receiving an input means the planner wired a child under a leaf node, which is an internal invariant violation, not a user-facing error.

Source

Thrown at src/batch/executors/src/executor/row_seq_scan.rs:90

            identity,
            metrics,
            table,
            scan_ranges,
            ordered,
            query_epoch,
            limit,
        }
    }
}

pub struct RowSeqScanExecutorBuilder {}

impl BoxedExecutorBuilder for RowSeqScanExecutorBuilder {
    async fn new_boxed_executor(
        source: &ExecutorBuilder<'_>,
        inputs: Vec<BoxedExecutor>,
    ) -> Result<BoxedExecutor> {
        ensure!(
            inputs.is_empty(),
            "Row sequential scan should not have input executor!"
        );
        let seq_scan_node = try_match_expand!(
            source.plan_node().get_node_body().unwrap(),
            NodeBody::RowSeqScan
        )?;

        let table_desc: &StorageTableDesc = seq_scan_node.get_table_desc()?;
        let column_ids = seq_scan_node
            .column_ids
            .iter()
            .copied()
            .map(ColumnId::from)
            .collect();
        let vnodes = match &seq_scan_node.vnode_bitmap {
            Some(vnodes) => Some(Bitmap::from(vnodes).into()),
            // This is possible for dml. vnode_bitmap is not filled by scheduler.

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Inspect the generated plan (EXPLAIN / plan debug output) and confirm the RowSeqScan node has no children; if it does, fix the planner code that builds it.
  2. Check that the frontend translates RowSeqScan into a leaf batch plan node with arity 0.
  3. If you are adding executors manually, ensure RowSeqScanExecutor is passed an empty inputs vec when calling the builder.
  4. Report as an internal bug with the query that triggered it if the planner looks correct.

Example fix

// before (planner builds scan with a child)
BatchPlanNode::new(RowSeqScan { table_ref }, vec![child])
// after (leaf node, no children)
BatchPlanNode::new(RowSeqScan { table_ref }, vec![])
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust (planner code): assert leaf arity before building
assert!(children.is_empty(), "RowSeqScan must be a leaf node");

Type guard

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

Try / catch

match exec.build(plan_node).await {
    Ok(exec) => exec,
    Err(e) if e.to_string().contains("should not have input executor") => {
        // log plan tree and report internal invariant violation
        e.to_string()
    }
}

Prevention

When it happens

Trigger: The batch executor builder framework dispatches to RowSeqScanExecutorBuilder with a non-empty `inputs` vector, i.e. the plan tree contains a RowSeqScan plan node that has children.

Common situations: A bug in the frontend/batch planner that attaches a child to a RowSeqScan node; manual construction of a plan fragment that feeds an executor into a seq scan; internal refactors that changed executor arity conventions.

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/7baacbb2cbd587a1. Report an issue: GitHub.