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

  1. Ensure all cluster nodes run the same RisingWave version (restart meta and compute nodes).
  2. Re-create the streaming job so the plan is re-serialized by the current binder.
  3. If this appears in a test, populate `after_match_skip` on the MatchRecognizeNode proto.
  4. 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

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


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