risingwavelabs/risingwave · error

unreachable

Error message

unreachable

What it means

`LogicalKafkaScan` implements `ToStream::to_stream` with a hard `unreachable!()`. The RisingWave stream optimizer is expected to rewrite Kafka scans into a `StreamSource`/source-backed plan (via DML/source conversion) before the to-stream phase reaches this node, so the conversion method itself is never supposed to be called. Hitting this panic means the frontend's pre-stream rewriting pipeline missed a case and let a `LogicalKafkaScan` survive into direct stream conversion.

Source

Thrown at src/frontend/src/optimizer/plan_node/logical_kafka_scan.rs:308

                },
            )
        }
    }
}

impl ToBatch for LogicalKafkaScan {
    fn to_batch(&self) -> Result<crate::optimizer::plan_node::BatchPlanRef> {
        let plan = BatchKafkaScan::new(self.core.clone(), self.kafka_timestamp_range).into();
        Ok(plan)
    }
}

impl ToStream for LogicalKafkaScan {
    fn to_stream(
        &self,
        _ctx: &mut ToStreamContext,
    ) -> Result<crate::optimizer::plan_node::StreamPlanRef> {
        unreachable!()
    }

    fn logical_rewrite_for_stream(
        &self,
        _ctx: &mut RewriteStreamContext,
    ) -> Result<(PlanRef, ColIndexMapping)> {
        unreachable!()
    }
}

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Check why the Kafka-scan rewrite (e.g. DML-to-source conversion in `LogicalOptimizer`) did not replace this node before to-stream; add/fix the rewrite rule so `LogicalKafkaScan` never reaches `to_stream`.
  2. If you construct `LogicalKafkaScan` in code or tests, run it through the standard logical optimization pipeline instead of calling `to_stream` directly.
  3. Reproduce with the offending SQL and file a RisingWave bug with the query plan (`EXPLAIN`) before the panic.
  4. As a local workaround, disable the feature/query shape that produces the Kafka scan until the rewrite is fixed.

Example fix

// before: node survives to to_stream and panics
let plan = logical_plan.to_stream(ctx)?; // panics: unreachable in LogicalKafkaScan
// after: ensure the rewrite pass ran first
let plan = LogicalOptimizer::gen_optimized_logical_plan_for_stream(logical_plan)?; // rewrites KafkaScan into source
let plan = plan.to_stream(ctx)?;
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure queries reach to_stream only through the full optimizer
assert!(!plan_contains::<LogicalKafkaScan>(&plan), "KafkaScan must be rewritten to source before to_stream");

Type guard

fn is_kafka_scan(node: &PlanNode) -> bool { matches!(node, PlanNode::LogicalKafkaScan(_)) }

Try / catch

match result {
    Err(e) if e.to_string().contains("unreachable") => log::error!("KafkaScan survived to to_stream; check rewrite pass: {e}"),
    other => other?,
}

Prevention

When it happens

Trigger: Calling `to_stream` on a plan tree that still contains a `LogicalKafkaScan` node, i.e. the optimization passes that should have replaced the Kafka scan with a source-based logical plan did not run or did not match this node.

Common situations: Internal frontend bugs: a new query shape introduces a Kafka scan path not covered by the rewriting pass in `LogicalOptimizer`; custom optimizer code or tests that construct `LogicalKafkaScan` directly and then invoke to-stream conversion; version changes where the rewrite rule was moved or renamed.

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