{"record":{"id":"af5834ce97ea58af","repo":"risingwavelabs/risingwave","slug":"core-predicate-must-exist","errorCode":null,"errorMessage":"core predicate must exist","messagePattern":"core predicate must exist","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/frontend/src/optimizer/plan_node/logical_join.rs","lineNumber":1531,"sourceCode":"    ) -> Result<StreamPlanRef> {\n        use super::stream::prelude::*;\n\n        if predicate.eq_keys().is_empty() {\n            return Err(ErrorCode::InvalidInputSyntax(\n                \"AsOf join requires at least 1 equal condition\".to_owned(),\n            )\n            .into());\n        }\n\n        let (left, right) = self.get_stream_input_for_hash_join(&predicate, ctx)?;\n        let left_len = left.schema().len();\n        let mut core = self.core.clone_with_inputs(left, right);\n        core.on = generic::JoinOn::EqPredicate(predicate);\n\n        let inequality_desc = Self::get_inequality_desc_from_predicate(\n            core.on\n                .as_eq_predicate_ref()\n                .expect(\"core predicate must exist\")\n                .other_cond()\n                .clone(),\n            left_len,\n        )?;\n\n        Ok(StreamAsOfJoin::new(core, inequality_desc)?.into())\n    }\n\n    /// Convert the logical join to a Hash join.\n    fn to_batch_hash_join(\n        &self,\n        logical_join: generic::Join<BatchPlanRef>,\n        predicate: EqJoinPredicate,\n    ) -> Result<BatchPlanRef> {\n        use super::batch::prelude::*;\n\n        let left_schema_len = logical_join.left.schema().len();\n        let asof_desc = self","sourceCodeStart":1513,"sourceCodeEnd":1549,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/frontend/src/optimizer/plan_node/logical_join.rs#L1513-L1549","documentation":"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.","triggerScenarios":"to_stream conversion of an ASOF join whose `on` predicate was rewritten to something other than JoinOn::EqPredicate before the extraction step.","commonSituations":"Custom optimizer rules or refactors changing JoinOn representation for asof joins; planner-produced asof joins after representation changes.","solutions":["Ensure the asof join core is built with generic::JoinOn::EqPredicate before calling as_eq_predicate_ref","Use as_condition_ref-based extraction that tolerates both representations","Verify upstream rules preserve the EqPredicate form for ASOF joins","Report as an optimizer regression with the MV definition"],"exampleFix":"// before\n.expect(\"core predicate must exist\")\n// after\n.ok_or_else(|| anyhow!(\"asof join core predicate missing\"))?","handlingStrategy":"type-guard","validationCode":"if core.on.as_eq_predicate_ref().is_none() { /* handle missing/other representation */ }","typeGuard":"fn eq_pred_of(on: &generic::JoinOn) -> Option<&EqJoinPredicate> { on.as_eq_predicate_ref() }","tryCatchPattern":null,"preventionTips":["Keep ASOF join cores in JoinOn::EqPredicate form","Check representation before extraction","Add regression tests for asof join stream conversion"],"tags":["streaming","asof-join","invariant","panic"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}