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
- Remove the NEXT-based DEFINE from the plan and express the logic with supported pattern/define constructs.
- Regenerate the plan with the standard frontend (re-create the query/MV).
- 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
- Never emit DefineSlotKind::Next from custom planners or tooling.
- Validate generated plans against supported DefineSlotKind values before submission.
- Document NEXT as unsupported in any MATCH_RECOGNIZE tooling.
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
- BatchMatchRecognize is not implemented yet
- MATCH_RECOGNIZE measure missing expression
- invalid MATCH_RECOGNIZE measure slot kind: {}
- MATCH_RECOGNIZE SUM/AVG measure slot missing agg_call
- MATCH_RECOGNIZE define missing condition
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/a022609684153a92.
Report an issue: GitHub.