risingwavelabs/risingwave · error

MATCH_RECOGNIZE measure missing expression

Error message

MATCH_RECOGNIZE measure missing expression

What it means

When deserializing a MATCH_RECOGNIZE executor from its protobuf plan, a measure slot carries no expression (`pb.expr` is None). The executor requires every measure to be backed by a concrete expression to build its evaluator, so it fails the deserialization instead of building a measure that computes nothing.

Source

Thrown at src/stream/src/executor/match_recognize/executor.rs:194

}

/// A `MEASURES` item compiled for execution.
pub struct CompiledMeasure {
    /// Expression over the synthetic per-match row: `InputRef(i)` reads `slots[i]`.
    expr: NonStrictExpression,
    slots: Vec<MeasureSlot>,
}

impl CompiledMeasure {
    /// Builds a compiled measure from its protobuf, building any aggregate kernels its slots need.
    pub fn from_protobuf(
        pb: &PbMatchRecognizeMeasure,
        error_report: impl EvalErrorReport + 'static,
    ) -> StreamExecutorResult<Self> {
        let expr = build_non_strict_from_prost(
            pb.expr
                .as_ref()
                .ok_or_else(|| anyhow::anyhow!("MATCH_RECOGNIZE measure missing expression"))?,
            error_report,
        )?;
        let slots = pb
            .slots
            .iter()
            .map(|s| {
                let kind = s.kind();
                // Fail fast rather than silently changing measure semantics under a corrupt plan
                // or version skew. (An out-of-range wire value decodes as UNSPECIFIED via
                // `s.kind()`.) Every later match on the kind relies on this rejection.
                if kind == MeasureSlotKind::Unspecified {
                    return Err(anyhow::anyhow!(
                        "invalid MATCH_RECOGNIZE measure slot kind: {}",
                        s.kind
                    )
                    .into());
                }
                let agg = match kind {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Regenerate the plan: drop and re-create the query/MV so the frontend emits a complete protobuf.
  2. Check frontend/backend version consistency; upgrade so both sides understand MATCH_RECOGNIZE measures.
  3. If writing a test or tool, always populate the expr field on PbMatchRecognizeMeasure.

Example fix

// before
let pb = PbMatchRecognizeMeasure { slots: ..., expr: None };
// after
let pb = PbMatchRecognizeMeasure { slots: ..., expr: Some(expr_proto) };
Defensive patterns

Strategy: validation

Validate before calling

// Validate before building the executor from protobuf:
if measure.expr.is_none() {
    return Err(anyhow!("measure '{}' has no expression", measure.name));
}

Try / catch

// Match on deserialization failure and fall back to re-planning:
match MeasureSlot::from_protobuf(&pb, &report) {
    Err(e) if e.to_string().contains("missing expression") => re_plan_or_recreate_job(),
    r => r,
}

Prevention

When it happens

Trigger: Calling MeasureSlot::from_protobuf with a PbMatchRecognizeMeasure whose expr field is unset/None.

Common situations: Corrupt or hand-edited plan fragments; protobuf produced by a frontend version mismatch where the field was expected to be filled; manually crafted prost messages 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/adc9c382e0cbe203. Report an issue: GitHub.