risingwavelabs/risingwave · error

LogicalIcebergIntermediateScan is only for batch queries

Error message

LogicalIcebergIntermediateScan is only for batch queries

What it means

LogicalIcebergIntermediateScan is designed exclusively for batch query plans (Iceberg query dispatch), so its `ToStream` implementation calls `unreachable!()`. Reaching it means the stream optimizer was invoked over a plan that should never have entered streaming planning — an internal invariant violation rather than a user-facing error.

Source

Thrown at src/frontend/src/optimizer/plan_node/logical_iceberg_intermediate_scan.rs:313

}

impl ToBatch for LogicalIcebergIntermediateScan {
    fn to_batch(&self) -> Result<crate::optimizer::plan_node::BatchPlanRef> {
        // This should not be called directly. The intermediate scan should be
        // converted to LogicalIcebergScan first via the materialization rule.
        Err(crate::error::ErrorCode::InternalError(
            "LogicalIcebergIntermediateScan should be converted to LogicalIcebergScan before to_batch".to_owned()
        )
        .into())
    }
}

impl ToStream for LogicalIcebergIntermediateScan {
    fn to_stream(
        &self,
        _ctx: &mut ToStreamContext,
    ) -> Result<crate::optimizer::plan_node::StreamPlanRef> {
        unreachable!("LogicalIcebergIntermediateScan is only for batch queries")
    }

    fn logical_rewrite_for_stream(
        &self,
        _ctx: &mut RewriteStreamContext,
    ) -> Result<(PlanRef, ColIndexMapping)> {
        unreachable!("LogicalIcebergIntermediateScan is only for batch queries")
    }
}

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure Iceberg intermediate scans are only produced within batch planning paths and never in stream planning.
  2. Change `unreachable!()` to a `bail!` with a descriptive error for more graceful handling.
  3. Reproduce with EXPLAIN and the query, and file a bug including the plan dump.

Example fix

// before
unreachable!("LogicalIcebergIntermediateScan is only for batch queries")

// after
bail!("LogicalIcebergIntermediateScan is only for batch queries")
Defensive patterns

Strategy: try-catch

Validate before calling

if streaming && plan.contains("LogicalIcebergIntermediateScan") {
    return Err("iceberg intermediate scan is batch-only".into());
}

Type guard

fn batch_only_node(plan: &LogicalPlan) -> bool {
    matches!(plan.node_type(), PlanNodeType::LogicalIcebergIntermediateScan)
}

Try / catch

match to_stream(plan) {
    Err(e) if e.to_string().contains("only for batch queries") =>
        route_to_batch_engine(query),
    r => r,
}

Prevention

When it happens

Trigger: Calling `to_stream` on a LogicalIcebergIntermediateScan, i.e. running stream planning (materialized view creation, streaming query) over a plan containing an Iceberg intermediate scan node.

Common situations: Optimizer bugs where an Iceberg batch-plan node leaks into the streaming pipeline; tests invoking ToStream generically over all logical plan nodes.

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