risingwavelabs/risingwave · error

MATCH_RECOGNIZE carries only one of the two WITHIN expressio

Error message

MATCH_RECOGNIZE carries only one of the two WITHIN expressions (predicate: {}, deadline: {}); the binder emits both or neither

What it means

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.

Source

Thrown at src/stream/src/from_proto/match_recognize.rs:166

        // per row. See `eval_deadline` in the executor.
        let within_deadline = node
            .within_deadline
            .as_ref()
            .map(|e| {
                build_non_strict_from_prost(
                    e,
                    DeadlineErrorReport::new(params.eval_error_report.clone()),
                )
            })
            .transpose()?;
        // The two WITHIN expressions are a correctness-coupled pair, and the coupling tightened when
        // the executor's span check started reading the cached deadline instead of evaluating the
        // predicate: `within` present with `within_deadline` absent now rejects EVERY candidate, so
        // the view would silently produce zero rows. The binder only ever emits both or neither
        // (`lower_within`), so a plan carrying one is corrupt — fail loud, as the rest of this
        // decoder does, rather than emitting nothing forever.
        if within.is_some() != within_deadline.is_some() {
            return Err(anyhow::anyhow!(
                "MATCH_RECOGNIZE carries only one of the two WITHIN expressions \
                 (predicate: {}, deadline: {}); the binder emits both or neither",
                within.is_some(),
                within_deadline.is_some(),
            )
            .into());
        }
        // The deadline is compared directly against the order key and the watermark
        // (`ScalarRefImpl::default_cmp` panics across variants — an actor crash loop that recovery
        // replays), and the span predicate is a boolean. The binder guarantees both
        // (`lower_within`); re-state it here so a skewed or corrupt plan fails at build time.
        let order_key_type = input
            .schema()
            .fields
            .get(order_key_indices[0])
            .map(|f| f.data_type())
            .ok_or_else(|| {
                anyhow::anyhow!(

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Align cluster node versions so producer and consumer agree on the WITHIN encoding (restart meta and compute nodes).
  2. Re-create the streaming job so the plan is regenerated with both fields.
  3. If constructing the node in a test, set both `within` and `within_deadline` (or neither).
  4. If reproducible on matching versions, report a plan-serialization bug.

Example fix

// before (test fixture)
MatchRecognizeNode { within: Some(predicate), within_deadline: None, .. }
// after
MatchRecognizeNode { within: Some(predicate), within_deadline: Some(deadline), .. }
Defensive patterns

Strategy: validation

Validate before calling

// Pair-check before executor construction
fn within_pair_ok(node: &MatchRecognizeNode) -> bool {
    node.within.is_some() == node.within_deadline.is_some()
}

Try / catch

// Fail with context rather than silently defaulting
if node.within.is_some() != node.within_deadline.is_some() {
    return Err(anyhow!("WITHIN predicate/deadline mismatch in MATCH_RECOGNIZE plan"));
}

Prevention

When it happens

Trigger: `new_boxed_executor` decodes a MatchRecognizeNode where the Option-ness of `within` and `within_deadline` differ (one is Some, the other None).

Common situations: 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.

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