{"record":{"id":"bc83e925fd32398d","repo":"risingwavelabs/risingwave","slug":"vectorindexnearest-should-have-an-input-executor","errorCode":null,"errorMessage":"VectorIndexNearest should have an input executor!","messagePattern":"VectorIndexNearest should have an input executor!","errorType":"validation","errorClass":"BatchError","httpStatus":null,"severity":"error","filePath":"src/batch/executors/src/executor/vector_index_nearest.rs","lineNumber":49,"sourceCode":"pub struct VectorIndexNearestExecutor<S: StateStore> {\n    identity: String,\n    schema: Schema,\n\n    input: BoxedExecutor,\n    query_epoch: BatchQueryEpoch,\n    vector_column_idx: usize,\n\n    reader: VectorIndexReader<S>,\n}\n\npub struct VectorIndexNearestExecutorBuilder {}\n\nimpl BoxedExecutorBuilder for VectorIndexNearestExecutorBuilder {\n    async fn new_boxed_executor(\n        source: &ExecutorBuilder<'_>,\n        inputs: Vec<BoxedExecutor>,\n    ) -> Result<BoxedExecutor> {\n        ensure!(\n            inputs.len() == 1,\n            \"VectorIndexNearest should have an input executor!\"\n        );\n        let [input]: [_; 1] = inputs.try_into().unwrap();\n        let vector_index_nearest_node = try_match_expand!(\n            source.plan_node().get_node_body().unwrap(),\n            NodeBody::VectorIndexNearest\n        )?;\n\n        dispatch_state_store!(source.context().state_store(), state_store, {\n            let reader = VectorIndexReader::new(\n                vector_index_nearest_node.reader_desc.as_ref().unwrap(),\n                state_store,\n            );\n\n            let mut schema = input.schema().clone();\n            schema.fields.push(Field::new(\n                \"vector_info\",","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/batch/executors/src/executor/vector_index_nearest.rs#L31-L67","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the plan: VectorIndexNearest must have exactly one child executor; fix the planner that built the node.","Ensure the required scan/source executor is attached as the single input of the node.","When constructing this executor manually, supply exactly one input in the inputs vector.","File an internal bug with the query if the plan looks correct."],"exampleFix":"// before (missing input)\nBatchPlanNode::new(NodeBody::VectorIndexNearest(node), vec![])\n// after (exactly one child)\nBatchPlanNode::new(NodeBody::VectorIndexNearest(node), vec![scan_executor])","handlingStrategy":"validation","validationCode":"// Rust: exactly one input required\nfn has_single_input(inputs: &[BoxedExecutor]) -> bool { inputs.len() == 1 }","typeGuard":"fn single_input(inputs: Vec<BoxedExecutor>) -> Option<BoxedExecutor> {\n    let [input]: [_; 1] = inputs.try_into().ok()?;\n    Some(input)\n}","tryCatchPattern":"match builder.new_boxed_executor(&src, inputs).await {\n    Err(e) if e.to_string().contains(\"VectorIndexNearest should have an input\") => {\n        // re-plan ensuring the scan child is attached\n    }\n    other => other,\n}","preventionTips":["Always attach exactly one scan/source child to VectorIndexNearest plan nodes.","Add planner tests for vector-index queries.","Validate arity in tests when changing the vector-index plan-node shape."],"tags":["batch-executor","vector-index","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"}