risingwavelabs/risingwave · error

invalid MATCH_RECOGNIZE measure slot kind: {}

Error message

invalid MATCH_RECOGNIZE measure slot kind: {}

What it means

A MATCH_RECOGNIZE measure slot's protobuf `kind` field decoded to UNSPECIFIED, meaning the wire value was unset or out of range. Since every later match on the kind relies on it being a concrete aggregation kind, the executor rejects it at deserialization instead of silently changing measure semantics.

Source

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

        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 {
                    MeasureSlotKind::Sum => {
                        let call =
                            AggCall::from_protobuf(s.agg_call.as_ref().ok_or_else(|| {
                                anyhow::anyhow!(
                                    "MATCH_RECOGNIZE SUM/AVG measure slot missing agg_call"
                                )
                            })?)?;
                        let col_type = call.args.arg_types()[0].clone();
                        let func = build_append_only(&call)?;
                        Some(AggSlot { func, col_type })
                    }
                    _ => None,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Align frontend and backend versions so all MeasureSlotKind values are recognized.
  2. Re-create the streaming job to regenerate a valid plan.
  3. In test code, always set a valid kind (Sum/Avg/etc.) on measure slots.

Example fix

// before
PbMeasureSlot { kind: 99 } // out of range -> UNSPECIFIED
// after
PbMeasureSlot { kind: MeasureSlotKind::Sum as i32 }
Defensive patterns

Strategy: validation

Validate before calling

// Check the kind decodes to a known value before use:
let kind = s.kind();
if kind == MeasureSlotKind::Unspecified {
    return Err(anyhow!("unsupported measure slot kind {}", s.kind));
}

Try / catch

// Catch enum rejection during plan load and regenerate the plan:
match slot_from_protobuf(&pb) {
    Err(e) if e.to_string().contains("invalid MATCH_RECOGNIZE measure slot kind") => regenerate_plan(),
    r => r,
}

Prevention

When it happens

Trigger: MeasureSlot::from_protobuf sees s.kind() == MeasureSlotKind::Unspecified, e.g. an out-of-range i32 in the kind field or kind simply not set.

Common situations: Version skew: newer frontend emits a kind value the older backend doesn't know; corrupt plan in meta storage; hand-built prost messages in tests.

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


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