{"record":{"id":"e705ab3eb7df08cf","repo":"risingwavelabs/risingwave","slug":"match-recognize-plan-carries-an-empty-order-by","errorCode":null,"errorMessage":"MATCH_RECOGNIZE plan carries an empty ORDER BY","messagePattern":"MATCH_RECOGNIZE plan carries an empty ORDER BY","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/stream/src/from_proto/match_recognize.rs","lineNumber":91,"sourceCode":"        let order_key_indices = node\n            .order_by\n            .iter()\n            .map(|c| {\n                let co = ColumnOrder::from_protobuf(c);\n                if co.order_type != OrderType::ascending() {\n                    return Err(anyhow::anyhow!(\n                        \"MATCH_RECOGNIZE only supports the default ascending ORDER BY, got {:?}\",\n                        co.order_type\n                    )\n                    .into());\n                }\n                Ok(co.column_index)\n            })\n            .collect::<StreamResult<Vec<usize>>>()?;\n        // The executor reads the leading ORDER BY column unconditionally; an empty list is a\n        // corrupt plan and must fail here, not index-panic there.\n        if order_key_indices.is_empty() {\n            return Err(anyhow::anyhow!(\"MATCH_RECOGNIZE plan carries an empty ORDER BY\").into());\n        }\n\n        let defines = node\n            .defines\n            .iter()\n            .map(|d| CompiledDefine::from_protobuf(d, params.eval_error_report.clone()))\n            .collect::<crate::executor::StreamExecutorResult<Vec<_>>>()?;\n        let measures = node\n            .measures\n            .iter()\n            .map(|m| CompiledMeasure::from_protobuf(m, params.eval_error_report.clone()))\n            .collect::<crate::executor::StreamExecutorResult<Vec<_>>>()?;\n\n        let pattern_node = node\n            .pattern_node\n            .as_ref()\n            .ok_or_else(|| anyhow::anyhow!(\"MATCH_RECOGNIZE node missing pattern\"))?;\n        let pattern = pattern_from_protobuf(pattern_node)","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/stream/src/from_proto/match_recognize.rs#L73-L109","documentation":"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.","triggerScenarios":"`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).","commonSituations":"Version skew where the frontend omitted the mandatory ORDER BY; corrupted plan fragments; tests constructing MATCH_RECOGNIZE nodes without setting `order_by`.","solutions":["Ensure the query includes a required ORDER BY clause in MATCH_RECOGNIZE, e.g. `ORDER BY event_time`.","Fix the frontend so it always emits a non-empty `order_by` (event time ordering is mandatory).","Re-plan/re-create the streaming job with matching frontend and compute versions.","In tests, populate `order_by` with at least one column index before building the executor."],"exampleFix":"// before\nlet node = MatchRecognizeNode { order_by: vec![], .. };\n// after\nlet node = MatchRecognizeNode { order_by: vec![column_order(0, OrderType::ascending())], .. };","handlingStrategy":"validation","validationCode":"// before constructing the executor\nif order_key_indices.is_empty() {\n    return Err(anyhow!(\"MATCH_RECOGNIZE plan carries an empty ORDER BY\"));\n}","typeGuard":"fn first_order_column(order_by: &[ColumnOrder]) -> Option<usize> { order_by.first().map(|c| c.column_index) }","tryCatchPattern":"let ts_idx = order_key_indices.first().copied().ok_or_else(|| anyhow!(\"MATCH_RECOGNIZE plan carries an empty ORDER BY\"))?;","preventionTips":["Always include ORDER BY in MATCH_RECOGNIZE queries.","Add a frontend invariant test: emitted MATCH_RECOGNIZE nodes have >= 1 order column.","Validate plan protos when deserializing actors, not only at executor build time."],"tags":["rust","streaming","match-recognize","plan-validation"],"backgroundTag":"empty-required-field","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}