{"record":{"id":"773b0dd998a758a1","repo":"risingwavelabs/risingwave","slug":"match-recognize-carries-only-one-of-the-two-within","errorCode":null,"errorMessage":"MATCH_RECOGNIZE carries only one of the two WITHIN expressions (predicate: {}, deadline: {}); the binder emits both or neither","messagePattern":"MATCH_RECOGNIZE carries only one of the two WITHIN expressions \\(predicate: (.+?), deadline: (.+?)\\); the binder emits both or neither","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/stream/src/from_proto/match_recognize.rs","lineNumber":166,"sourceCode":"        // per row. See `eval_deadline` in the executor.\n        let within_deadline = node\n            .within_deadline\n            .as_ref()\n            .map(|e| {\n                build_non_strict_from_prost(\n                    e,\n                    DeadlineErrorReport::new(params.eval_error_report.clone()),\n                )\n            })\n            .transpose()?;\n        // The two WITHIN expressions are a correctness-coupled pair, and the coupling tightened when\n        // the executor's span check started reading the cached deadline instead of evaluating the\n        // predicate: `within` present with `within_deadline` absent now rejects EVERY candidate, so\n        // the view would silently produce zero rows. The binder only ever emits both or neither\n        // (`lower_within`), so a plan carrying one is corrupt — fail loud, as the rest of this\n        // decoder does, rather than emitting nothing forever.\n        if within.is_some() != within_deadline.is_some() {\n            return Err(anyhow::anyhow!(\n                \"MATCH_RECOGNIZE carries only one of the two WITHIN expressions \\\n                 (predicate: {}, deadline: {}); the binder emits both or neither\",\n                within.is_some(),\n                within_deadline.is_some(),\n            )\n            .into());\n        }\n        // The deadline is compared directly against the order key and the watermark\n        // (`ScalarRefImpl::default_cmp` panics across variants — an actor crash loop that recovery\n        // replays), and the span predicate is a boolean. The binder guarantees both\n        // (`lower_within`); re-state it here so a skewed or corrupt plan fails at build time.\n        let order_key_type = input\n            .schema()\n            .fields\n            .get(order_key_indices[0])\n            .map(|f| f.data_type())\n            .ok_or_else(|| {\n                anyhow::anyhow!(","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/stream/src/from_proto/match_recognize.rs#L148-L184","documentation":"MATCH_RECOGNIZE WITHIN support is expressed as a pair: a span predicate (`within`) and a cached deadline expression (`within_deadline`). The binder always emits both or neither; the executor detects a plan carrying exactly one and rejects it. This guards against a silent behavior change: with only `within` present, the executor would evaluate the deadline path and reject every candidate row, making the view return zero rows forever.","triggerScenarios":"`new_boxed_executor` decodes a MatchRecognizeNode where the Option-ness of `within` and `within_deadline` differ (one is Some, the other None).","commonSituations":"Version skew between a producer that serializes only one field and a consumer that requires both; corrupted plan persistence; a hand-built proto in tests missing one of the two fields.","solutions":["Align cluster node versions so producer and consumer agree on the WITHIN encoding (restart meta and compute nodes).","Re-create the streaming job so the plan is regenerated with both fields.","If constructing the node in a test, set both `within` and `within_deadline` (or neither).","If reproducible on matching versions, report a plan-serialization bug."],"exampleFix":"// before (test fixture)\nMatchRecognizeNode { within: Some(predicate), within_deadline: None, .. }\n// after\nMatchRecognizeNode { within: Some(predicate), within_deadline: Some(deadline), .. }","handlingStrategy":"validation","validationCode":"// Pair-check before executor construction\nfn within_pair_ok(node: &MatchRecognizeNode) -> bool {\n    node.within.is_some() == node.within_deadline.is_some()\n}","typeGuard":null,"tryCatchPattern":"// Fail with context rather than silently defaulting\nif node.within.is_some() != node.within_deadline.is_some() {\n    return Err(anyhow!(\"WITHIN predicate/deadline mismatch in MATCH_RECOGNIZE plan\"));\n}","preventionTips":["Emit both WITHIN fields from a single code path (binder's lower_within) so they can't diverge.","Add a proto-level invariant test that within.is_some() == within_deadline.is_some().","Avoid rolling upgrades across WITHIN-encoding changes."],"tags":["streaming","match-recognize","invariant","within-clause"],"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-14T11:17:12.474Z"}