risingwavelabs/risingwave · error · BatchError

VectorIndexNearest should have an input executor!

Error message

VectorIndexNearest should have an input executor!

What it means

VectorIndexNearestExecutorBuilder requires exactly one input executor. The nearest-neighbor lookup over a vector index consumes the candidate rows produced by an upstream executor (e.g. a scan), so the builder asserts inputs.len() == 1. Zero or multiple inputs indicate a malformed plan.

Source

Thrown at src/batch/executors/src/executor/vector_index_nearest.rs:49

pub struct VectorIndexNearestExecutor<S: StateStore> {
    identity: String,
    schema: Schema,

    input: BoxedExecutor,
    query_epoch: BatchQueryEpoch,
    vector_column_idx: usize,

    reader: VectorIndexReader<S>,
}

pub struct VectorIndexNearestExecutorBuilder {}

impl BoxedExecutorBuilder for VectorIndexNearestExecutorBuilder {
    async fn new_boxed_executor(
        source: &ExecutorBuilder<'_>,
        inputs: Vec<BoxedExecutor>,
    ) -> Result<BoxedExecutor> {
        ensure!(
            inputs.len() == 1,
            "VectorIndexNearest should have an input executor!"
        );
        let [input]: [_; 1] = inputs.try_into().unwrap();
        let vector_index_nearest_node = try_match_expand!(
            source.plan_node().get_node_body().unwrap(),
            NodeBody::VectorIndexNearest
        )?;

        dispatch_state_store!(source.context().state_store(), state_store, {
            let reader = VectorIndexReader::new(
                vector_index_nearest_node.reader_desc.as_ref().unwrap(),
                state_store,
            );

            let mut schema = input.schema().clone();
            schema.fields.push(Field::new(
                "vector_info",

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Verify the plan: VectorIndexNearest must have exactly one child executor; fix the planner that built the node.
  2. Ensure the required scan/source executor is attached as the single input of the node.
  3. When constructing this executor manually, supply exactly one input in the inputs vector.
  4. File an internal bug with the query if the plan looks correct.

Example fix

// before (missing input)
BatchPlanNode::new(NodeBody::VectorIndexNearest(node), vec![])
// after (exactly one child)
BatchPlanNode::new(NodeBody::VectorIndexNearest(node), vec![scan_executor])
Defensive patterns

Strategy: validation

Validate before calling

// Rust: exactly one input required
fn has_single_input(inputs: &[BoxedExecutor]) -> bool { inputs.len() == 1 }

Type guard

fn single_input(inputs: Vec<BoxedExecutor>) -> Option<BoxedExecutor> {
    let [input]: [_; 1] = inputs.try_into().ok()?;
    Some(input)
}

Try / catch

match builder.new_boxed_executor(&src, inputs).await {
    Err(e) if e.to_string().contains("VectorIndexNearest should have an input") => {
        // re-plan ensuring the scan child is attached
    }
    other => other,
}

Prevention

When it happens

Trigger: new_boxed_executor is called where inputs.len() != 1 for a VectorIndexNearest plan node — typically zero children because the planner did not attach the source scan, or more than one child.

Common situations: Planner bugs when planning vector-index queries (missing the required scan child or attaching extra children); hand-built plan trees with wrong arity; version changes in the vector-index plan-node shape.

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