risingwavelabs/risingwave · error

MATCH_RECOGNIZE node missing pattern

Error message

MATCH_RECOGNIZE node missing pattern

What it means

A MATCH_RECOGNIZE plan must carry a pattern node describing the regex-like row pattern to match. `new_boxed_executor` fails with this error when `node.pattern_node` is `None`, since the executor cannot compile an NFA without a pattern. The pattern is mandatory infrastructure for the operator, so its absence indicates a corrupt or incomplete plan.

Source

Thrown at src/stream/src/from_proto/match_recognize.rs:108

        if order_key_indices.is_empty() {
            return Err(anyhow::anyhow!("MATCH_RECOGNIZE plan carries an empty ORDER BY").into());
        }

        let defines = node
            .defines
            .iter()
            .map(|d| CompiledDefine::from_protobuf(d, params.eval_error_report.clone()))
            .collect::<crate::executor::StreamExecutorResult<Vec<_>>>()?;
        let measures = node
            .measures
            .iter()
            .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() {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure the SQL MATCH_RECOGNIZE clause includes a PATTERN clause so the frontend emits `pattern_node`.
  2. Re-create the streaming job with a frontend version matching the compute node.
  3. Inspect the serialized actor plan and verify `pattern_node` is populated.
  4. In tests, always set `pattern_node` via `pattern_from_protobuf`-compatible structures before building.

Example fix

// before
let node = MatchRecognizeNode { pattern_node: None, .. };
// after
let node = MatchRecognizeNode { pattern_node: Some(pattern_node_proto), .. };
Defensive patterns

Strategy: validation

Validate before calling

// before building
if node.pattern_node.is_none() {
    return Err(anyhow!("MATCH_RECOGNIZE node missing pattern"));
}

Type guard

fn has_pattern(node: &MatchRecognizeNode) -> bool { node.pattern_node.is_some() }

Try / catch

let pattern_node = node.pattern_node.as_ref().ok_or_else(|| anyhow::anyhow!("MATCH_RECOGNIZE node missing pattern"))?;

Prevention

When it happens

Trigger: `new_boxed_executor` calls `node.pattern_node.as_ref().ok_or_else(...)` and the protobuf oneof/optional `pattern_node` field is unset — plan serialization dropped it, the frontend never set it, or the proto was truncated/edited.

Common situations: Version skew where an older frontend omitted the pattern field; corrupted actor protos in-flight; tests constructing MATCH_RECOGNIZE nodes without `pattern_node`; proto schema evolution mistakes.

Related errors


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