risingwavelabs/risingwave · error

core predicate must exist

Error message

core predicate must exist

What it means

to_stream_asof_join stores an EqPredicate in the core and then immediately unwraps as_eq_predicate_ref() with expect("core predicate must exist"). If the predicate is not an EqPredicate representation, the ASOF join cannot extract its inequality condition and panics.

Solutions

  1. Ensure the asof join core is built with generic::JoinOn::EqPredicate before calling as_eq_predicate_ref
  2. Use as_condition_ref-based extraction that tolerates both representations
  3. Verify upstream rules preserve the EqPredicate form for ASOF joins
  4. Report as an optimizer regression with the MV definition

Example fix

// before
.expect("core predicate must exist")
// after
.ok_or_else(|| anyhow!("asof join core predicate missing"))?
Defensive patterns

Strategy: type-guard

Validate before calling

if core.on.as_eq_predicate_ref().is_none() { /* handle missing/other representation */ }

Type guard

fn eq_pred_of(on: &generic::JoinOn) -> Option<&EqJoinPredicate> { on.as_eq_predicate_ref() }

Prevention

When it happens

Trigger: to_stream conversion of an ASOF join whose `on` predicate was rewritten to something other than JoinOn::EqPredicate before the extraction step.

Common situations: Custom optimizer rules or refactors changing JoinOn representation for asof joins; planner-produced asof joins after representation changes.

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

Appendix: source

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

    ) -> Result<StreamPlanRef> {
        use super::stream::prelude::*;

        if predicate.eq_keys().is_empty() {
            return Err(ErrorCode::InvalidInputSyntax(
                "AsOf join requires at least 1 equal condition".to_owned(),
            )
            .into());
        }

        let (left, right) = self.get_stream_input_for_hash_join(&predicate, ctx)?;
        let left_len = left.schema().len();
        let mut core = self.core.clone_with_inputs(left, right);
        core.on = generic::JoinOn::EqPredicate(predicate);

        let inequality_desc = Self::get_inequality_desc_from_predicate(
            core.on
                .as_eq_predicate_ref()
                .expect("core predicate must exist")
                .other_cond()
                .clone(),
            left_len,
        )?;

        Ok(StreamAsOfJoin::new(core, inequality_desc)?.into())
    }

    /// Convert the logical join to a Hash join.
    fn to_batch_hash_join(
        &self,
        logical_join: generic::Join<BatchPlanRef>,
        predicate: EqJoinPredicate,
    ) -> Result<BatchPlanRef> {
        use super::batch::prelude::*;

        let left_schema_len = logical_join.left.schema().len();
        let asof_desc = self

View on GitHub (pinned to 6469eb736d)