risingwavelabs/risingwave · error

index scan failed to batch

Error message

index scan failed to batch

What it means

During index-lookup-join conversion, the right-side index scan is lowered to a batch plan with to_batch() and the result is expect()-ed. If the index scan cannot be converted to a batch node (no valid batch implementation), the optimizer panics instead of skipping the index.

Solutions

  1. Check the index selection logic: ensure only indexes that fully cover the required columns are selected
  2. Verify the table and index catalogs are consistent (recreate index if stale)
  3. Fall back to a regular hash join instead of forcing the lookup-join path when to_batch returns None
  4. Report with the query plan; this indicates an optimizer rule missing a feasibility check

Example fix

// before
new_batch_join.right = index_scan.to_batch().expect("index scan failed to batch");
// after
match index_scan.to_batch() {
    Some(right) => new_batch_join.right = right,
    None => continue, // try next index or fall back to non-lookup join
}
Defensive patterns

Strategy: fallback

Validate before calling

// before lookup-join conversion, verify the index covers required columns and its scan converts:
// if logical_scan.to_index_scan_if_index_covered(index).and_then(|s| s.to_batch()).is_none() { skip index }

Try / catch

match index_scan.to_batch() { Some(p) => p, None => fallback_to_hash_join() }

Prevention

When it happens

Trigger: to_batch_lookup_join_with_index_selection encounters an index whose LogicalScan cannot produce a batch plan via to_index_scan_if_index_covered + to_batch, e.g. due to unsupported scan features or missing table/catalog info.

Common situations: Queries triggering batch index lookup joins where the index's primary/table structure changed, or a new scan feature (e.g. unsupported filter pushdown) makes the index scan unconvertible.

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

Appendix: source

Thrown at src/frontend/src/optimizer/plan_node/logical_join.rs:312

        {
            result_plan = Some(lookup_join);
        }

        if self
            .core
            .ctx()
            .session_ctx()
            .config()
            .enable_index_selection()
        {
            let indexes = logical_scan.table_indexes();
            for index in indexes {
                if let Some(index_scan) = logical_scan.to_index_scan_if_index_covered(index) {
                    let index_scan: PlanRef = index_scan.into();
                    let that = self.clone_with_left_right(self.left(), index_scan.clone());
                    let mut new_batch_join = batch_join.clone();
                    new_batch_join.right =
                        index_scan.to_batch().expect("index scan failed to batch");

                    // Lookup covered index.
                    if let Some(lookup_join) =
                        that.to_batch_lookup_join(predicate.clone(), new_batch_join)?
                    {
                        match &result_plan {
                            None => result_plan = Some(lookup_join),
                            Some(prev_lookup_join) => {
                                // Prefer to choose lookup join with longer lookup prefix len.
                                if prev_lookup_join.lookup_prefix_len()
                                    < lookup_join.lookup_prefix_len()
                                {
                                    result_plan = Some(lookup_join)
                                }
                            }
                        }
                    }
                }

View on GitHub (pinned to 6469eb736d)