risingwavelabs/risingwave · error

physical NEXT in a MATCH_RECOGNIZE DEFINE is not supported

Error message

physical NEXT in a MATCH_RECOGNIZE DEFINE is not supported

What it means

A MATCH_RECOGNIZE DEFINE slot declares the physical NEXT kind, i.e. a direct read of the next row. This is watermark-unsafe and arrival-order-dependent and no plan the frontend produces should carry it, so the executor rejects it outright rather than evaluating a skewed predicate.

Source

Thrown at src/stream/src/executor/match_recognize/executor.rs:372

        let condition = build_non_strict_from_prost(
            pb.condition
                .as_ref()
                .ok_or_else(|| anyhow::anyhow!("MATCH_RECOGNIZE define missing condition"))?,
            error_report,
        )?;
        let slots = pb
            .slots
            .iter()
            .map(|s| {
                let kind = s.kind();
                // The binder rejects physical NEXT in DEFINE (a verdict depending on rows after
                // the candidate needs per-candidate decidability), so no plan this frontend
                // produces carries it — reject rather than evaluate a watermark-unsafe,
                // arrival-order-dependent read from a skewed plan. UNSPECIFIED (also what an
                // out-of-range wire value decodes to) fails fast rather than silently changing
                // the predicate's meaning. Every later match on the kind relies on this.
                if kind == DefineSlotKind::Next {
                    return Err(StreamExecutorError::from(anyhow::anyhow!(
                        "physical NEXT in a MATCH_RECOGNIZE DEFINE is not supported"
                    )));
                }
                if kind == DefineSlotKind::Unspecified {
                    return Err(StreamExecutorError::from(anyhow::anyhow!(
                        "invalid MATCH_RECOGNIZE define slot kind: {}",
                        s.kind
                    )));
                }
                Ok(DefineSlot {
                    kind,
                    vars: s.vars.clone(),
                    col_idx: s.col_idx as usize,
                    offset: s.offset as usize,
                })
            })
            .collect::<StreamExecutorResult<Vec<_>>>()?;
        Ok(CompiledDefine {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Remove the NEXT-based DEFINE from the plan and express the logic with supported pattern/define constructs.
  2. Regenerate the plan with the standard frontend (re-create the query/MV).
  3. Fix any custom tooling that emits DefineSlotKind::Next in plan protobufs.

Example fix

// before
PbDefineSlot { kind: DefineSlotKind::Next as i32, .. }
// after
PbDefineSlot { kind: DefineSlotKind::Expr as i32, .. }
Defensive patterns

Strategy: validation

Validate before calling

// Reject NEXT defines before sending the plan to the backend:
if s.kind() == DefineSlotKind::Next {
    return Err(anyhow!("NEXT define is not supported; rewrite the pattern"));
}

Try / catch

// Surface an unsupported-plan error to the user instead of an internal failure:
match define_from_protobuf(&pb) {
    Err(e) if e.to_string().contains("physical NEXT") => {
        return user_facing_unsupported_feature_error(e);
    }
    r => r,
}

Prevention

When it happens

Trigger: DefineSlot::from_protobuf sees kind == DefineSlotKind::Next, from a corrupt/hand-modified plan or a non-standard frontend emitting NEXT.

Common situations: Custom or experimental frontend emitting NEXT defines; hand-crafted protobufs in tests; plan corruption in meta storage.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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