risingwavelabs/risingwave · error

LogicalVectorSearchLookupJoin does not support input that…

Error message

LogicalVectorSearchLookupJoin does not support input that can possibly be rewritten

What it means

Raised in LogicalVectorSearchLookupJoin::logical_rewrite_for_stream when the input's stream rewrite is not identity (the input would be rewritten, e.g. introducing shares or length changes). The lookup-join executor requires a stable, unrewritten input, so streaming conversion is rejected with a TODO to support it.

Solutions

  1. Restructure the streaming query so the lookup join's input does not require logical stream rewriting (no shared subplans/rewrites).
  2. Alternatively extend the optimizer to support rewritten inputs for vector search lookup joins.
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at src/frontend/src/optimizer/plan_node/logical_vector_search_lookup_join.rs:291 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/8e6c8c7e73f78fa4. Report an issue: GitHub.

Appendix: source

Thrown at src/frontend/src/optimizer/plan_node/logical_vector_search_lookup_join.rs:291

        let core = self.core.clone_with_input(input, lookup);
        LogicalFilter::create(Self::with_core(core).into(), predicate)
    }
}

impl ToStream for LogicalVectorSearchLookupJoin {
    fn logical_rewrite_for_stream(
        &self,
        ctx: &mut RewriteStreamContext,
    ) -> crate::error::Result<(PlanRef, ColIndexMapping)> {
        if !self
            .core
            .input
            .logical_rewrite_for_stream(ctx)?
            .1
            .is_identity()
        {
            // TODO: support it
            bail!(
                "LogicalVectorSearchLookupJoin does not support input that can possibly be rewritten"
            )
        }
        Ok((
            self.clone().into(),
            ColIndexMapping::identity(self.base.schema().len()),
        ))
    }

    fn to_stream(&self, ctx: &mut ToStreamContext) -> Result<StreamPlanRef> {
        if let Some(core) = self.to_vector_index_lookup_join(|plan| plan.to_stream(ctx))? {
            // `ProcessTimeBroadcast` is intentionally excluded: the vector-index lookup executor
            // does not consume a broadcast change stream or maintain replicated lookup state.
            if !matches!(&core.as_of, Some(AsOf::ProcessTime)) {
                bail!("streaming vector index lookup join must be proctime temporal join");
            }
            return Ok(StreamVectorIndexLookupJoin::new(core)?.into());
        }

View on GitHub (pinned to 6469eb736d)