risingwavelabs/risingwave · error

MATCH_RECOGNIZE plan carries an empty ORDER BY

Error message

MATCH_RECOGNIZE plan carries an empty ORDER BY

What it means

The MATCH_RECOGNIZE executor unconditionally reads the leading ORDER BY column to establish event-time ordering, so an empty ORDER BY list would cause an index panic later. `new_boxed_executor` validates the decoded `order_key_indices` and returns this error instead, treating an empty ORDER BY as a corrupt plan fragment.

Source

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

        let order_key_indices = node
            .order_by
            .iter()
            .map(|c| {
                let co = ColumnOrder::from_protobuf(c);
                if co.order_type != OrderType::ascending() {
                    return Err(anyhow::anyhow!(
                        "MATCH_RECOGNIZE only supports the default ascending ORDER BY, got {:?}",
                        co.order_type
                    )
                    .into());
                }
                Ok(co.column_index)
            })
            .collect::<StreamResult<Vec<usize>>>()?;
        // The executor reads the leading ORDER BY column unconditionally; an empty list is a
        // corrupt plan and must fail here, not index-panic there.
        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)

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure the query includes a required ORDER BY clause in MATCH_RECOGNIZE, e.g. `ORDER BY event_time`.
  2. Fix the frontend so it always emits a non-empty `order_by` (event time ordering is mandatory).
  3. Re-plan/re-create the streaming job with matching frontend and compute versions.
  4. In tests, populate `order_by` with at least one column index before building the executor.

Example fix

// before
let node = MatchRecognizeNode { order_by: vec![], .. };
// after
let node = MatchRecognizeNode { order_by: vec![column_order(0, OrderType::ascending())], .. };
Defensive patterns

Strategy: validation

Validate before calling

// before constructing the executor
if order_key_indices.is_empty() {
    return Err(anyhow!("MATCH_RECOGNIZE plan carries an empty ORDER BY"));
}

Type guard

fn first_order_column(order_by: &[ColumnOrder]) -> Option<usize> { order_by.first().map(|c| c.column_index) }

Try / catch

let ts_idx = order_key_indices.first().copied().ok_or_else(|| anyhow!("MATCH_RECOGNIZE plan carries an empty ORDER BY"))?;

Prevention

When it happens

Trigger: `new_boxed_executor` decodes `node.order_by` into an empty `Vec<usize>` — the plan proto carried no ORDER BY entries for the MATCH_RECOGNIZE node (missing field, planner bug, or hand-trimmed proto).

Common situations: Version skew where the frontend omitted the mandatory ORDER BY; corrupted plan fragments; tests constructing MATCH_RECOGNIZE nodes without setting `order_by`.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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