risingwavelabs/risingwave · error
invalid MATCH_RECOGNIZE after_match_skip mode: {}
Error message
invalid MATCH_RECOGNIZE after_match_skip mode: {} What it means
The proto enum `after_match_skip.mode` is `Unspecified`, meaning the producer never set a valid skip mode. The decoder maps PastLastRow/ToNextRow/ToFirst/ToLast to executor `SkipMode`s and treats Unspecified as invalid input. Proto3 leaves scalar/enum defaults at 0, so this usually indicates an unset field.
Source
Thrown at src/stream/src/from_proto/match_recognize.rs:132
// would mask a corrupt plan or a version skew.
let skip = {
use risingwave_pb::stream_plan::match_recognize_after_match_skip::Mode;
let pb_skip = node
.after_match_skip
.as_ref()
.ok_or_else(|| anyhow::anyhow!("MATCH_RECOGNIZE node missing after_match_skip"))?;
let target = || {
pb_skip.target.clone().ok_or_else(|| {
anyhow::anyhow!("AFTER MATCH SKIP TO FIRST/LAST missing its target variable")
})
};
match pb_skip.mode() {
Mode::PastLastRow => SkipMode::PastLastRow,
Mode::ToNextRow => SkipMode::ToNextRow,
Mode::ToFirst => SkipMode::ToFirst(target()?),
Mode::ToLast => SkipMode::ToLast(target()?),
Mode::Unspecified => {
return Err(anyhow::anyhow!(
"invalid MATCH_RECOGNIZE after_match_skip mode: {}",
pb_skip.mode
)
.into());
}
}
};
let within = node
.within
.as_ref()
.map(|e| build_non_strict_from_prost(e, params.eval_error_report.clone()))
.transpose()?;
// Over `DeadlineErrorReport`, not the actor's report directly: `first + bound` leaving the
// order key's range is the window that never closes, not a compute error to count and log
// per row. See `eval_deadline` in the executor.
let within_deadline = node
.within_deadlineView on GitHub (pinned to 6469eb736d)
Solutions
- Set an explicit mode on the AfterMatchSkip proto (e.g. `mode: PAST_LAST_ROW`).
- If writing SQL, add an `AFTER MATCH SKIP ...` clause so the binder emits a concrete mode.
- Re-create the streaming job to regenerate the plan with a current binder.
- Check for version skew between producer and consumer nodes.
Example fix
// before
AfterMatchSkip { mode: Mode::Unspecified as i32, target: None }
// after
AfterMatchSkip { mode: Mode::PastLastRow as i32, target: None } Defensive patterns
Strategy: validation
Validate before calling
// Check the mode is a known value before decoding
fn skip_mode_valid(skip: &AfterMatchSkip) -> bool {
use risingwave_pb::stream_plan::match_recognize_after_match_skip::Mode;
!matches!(skip.mode(), Mode::Unspecified)
} Try / catch
// Wrap decode errors with the offending raw value
let mode = Mode::try_from(pb_skip.mode)
.map_err(|e| anyhow!("unknown after_match_skip mode {}: {e}", pb_skip.mode))?; Prevention
- Always set the mode explicitly when constructing AfterMatchSkip protos.
- In SQL, include an AFTER MATCH SKIP clause; don't rely on proto defaults.
- Use exhaustive match on Mode in producers so new variants are handled.
When it happens
Trigger: `new_boxed_executor` receives an `after_match_skip` whose `mode` is `MODE_UNSPECIFIED` (0) — typically from a hand-built proto, an older producer that set the message but not the mode, or corrupted serialization.
Common situations: Version skew after adding the skip enum; test fixtures constructing AfterMatchSkip without setting `mode`; plans from a foreign/hand-crafted producer.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- invalid MATCH_RECOGNIZE measure slot kind: {}
- invalid MATCH_RECOGNIZE define slot kind: {}
- unknown MATCH_RECOGNIZE input mode: {}
- unsupported MATCH_RECOGNIZE input mode: {other:?}
- invalid MATCH_RECOGNIZE pattern: {e}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/51a572ecb2a99723.
Report an issue: GitHub.