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
- Regenerate the plan: drop and re-create the query/MV so the frontend emits a complete protobuf.
- Check frontend/backend version consistency; upgrade so both sides understand MATCH_RECOGNIZE measures.
- 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
- Always construct MATCH_RECOGNIZE plans through the standard frontend.
- In tests, use builder helpers that enforce expr is Some.
- Pin frontend and backend to the same version.
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
- invalid MATCH_RECOGNIZE measure slot kind: {}
- MATCH_RECOGNIZE SUM/AVG measure slot missing agg_call
- MATCH_RECOGNIZE define missing condition
- invalid MATCH_RECOGNIZE define slot kind: {}
- unspecified AsOf join inequality type
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/adc9c382e0cbe203.
Report an issue: GitHub.