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
- Check the index selection logic: ensure only indexes that fully cover the required columns are selected
- Verify the table and index catalogs are consistent (recreate index if stale)
- Fall back to a regular hash join instead of forcing the lookup-join path when to_batch returns None
- 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
- Only select fully covering indexes for lookup joins
- Keep table/index catalogs in sync
- Treat to_batch()==None as skip, not panic, in optimizer rules
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)