risingwavelabs/risingwave · error
MATCH_RECOGNIZE node missing after_match_skip
Error message
MATCH_RECOGNIZE node missing after_match_skip
What it means
The MATCH_RECOGNIZE stream plan proto carries no `after_match_skip` field, which the decoder requires. Every plan produced by the binder must state how matches are skipped (PAST LAST ROW, TO NEXT ROW, TO FIRST/LAST), so its absence means a corrupt or partially serialized plan. The decoder fails fast instead of guessing a default, which would silently change query semantics.
Source
Thrown at src/stream/src/from_proto/match_recognize.rs:120
.map(|m| CompiledMeasure::from_protobuf(m, params.eval_error_report.clone()))
.collect::<crate::executor::StreamExecutorResult<Vec<_>>>()?;
let pattern_node = node
.pattern_node
.as_ref()
.ok_or_else(|| anyhow::anyhow!("MATCH_RECOGNIZE node missing pattern"))?;
let pattern = pattern_from_protobuf(pattern_node)
.map_err(|e| anyhow::anyhow!("invalid MATCH_RECOGNIZE pattern: {e}"))?;
let nfa = Nfa::compile(&pattern);
// Fail fast on anything malformed rather than silently defaulting to PAST LAST ROW, which
// 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());
}
}View on GitHub (pinned to 6469eb736d)
Solutions
- Ensure all cluster nodes run the same RisingWave version (restart meta and compute nodes).
- Re-create the streaming job so the plan is re-serialized by the current binder.
- If this appears in a test, populate `after_match_skip` on the MatchRecognizeNode proto.
- If reproducible on identical versions, report a serialization bug.
Example fix
// before (test constructing the node)
let node = MatchRecognizeNode { pattern_node: Some(pattern), ..Default::default() };
// after
let node = MatchRecognizeNode {
pattern_node: Some(pattern),
after_match_skip: Some(AfterMatchSkip { mode: Mode::PastLastRow as i32, target: None }),
..Default::default()
}; Defensive patterns
Strategy: validation
Validate before calling
// Validate the node before handing it to the executor
fn skip_present(node: &MatchRecognizeNode) -> Result<(), String> {
if node.after_match_skip.is_some() { Ok(()) } else { Err("after_match_skip missing".into()) }
} Try / catch
// Catch executor-build failure and include sink/plan identity in logs
match MatchRecognizeExecutor::new(...) {
Ok(e) => e,
Err(err) => return Err(err.context("decoding MATCH_RECOGNIZE plan")),
} Prevention
- Never construct MatchRecognizeNode protos by hand without after_match_skip.
- Use Default-derived builders that force explicit mode setting.
- Keep producer and consumer versions aligned.
When it happens
Trigger: `new_boxed_executor` receives a `MatchRecognizeNode` proto where `after_match_skip` is `None` while `pattern_node` is set — e.g. truncated proto serialization or a producer that predates the skip clause.
Common situations: Rolling upgrade with an older meta node emitting plans without the skip field while the compute node requires it; corrupted plan persistence; manual plan injection in tests.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- unknown MATCH_RECOGNIZE input mode: {}
- invalid MATCH_RECOGNIZE pattern: {e}
- AFTER MATCH SKIP TO FIRST/LAST missing its target variable
- invalid MATCH_RECOGNIZE after_match_skip mode: {}
- query_epoch not set in distributed lookup join
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/a30bd40fbcd9bb5c.
Report an issue: GitHub.