{"record":{"id":"d95a940cf66db4b7","repo":"risingwavelabs/risingwave","slug":"frame-end-cannot-be-unbounded-preceding","errorCode":null,"errorMessage":"frame end cannot be UNBOUNDED PRECEDING","messagePattern":"frame end cannot be UNBOUNDED PRECEDING","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/core/src/window_function/call.rs","lineNumber":219,"sourceCode":"    Following(T),\n    UnboundedFollowing,\n}\n\nimpl<T> FrameBound<T> {\n    fn offset_value(&self) -> Option<&T> {\n        match self {\n            UnboundedPreceding | UnboundedFollowing | CurrentRow => None,\n            Preceding(offset) | Following(offset) => Some(offset),\n        }\n    }\n\n    pub(super) fn validate_bounds(\n        start: &Self,\n        end: &Self,\n        offset_checker: impl Fn(&T) -> Result<()>,\n    ) -> Result<()> {\n        match (start, end) {\n            (_, UnboundedPreceding) => bail!(\"frame end cannot be UNBOUNDED PRECEDING\"),\n            (UnboundedFollowing, _) => {\n                bail!(\"frame start cannot be UNBOUNDED FOLLOWING\")\n            }\n            (Following(_), CurrentRow) | (Following(_), Preceding(_)) => {\n                bail!(\"frame starting from following row cannot have preceding rows\")\n            }\n            (CurrentRow, Preceding(_)) => {\n                bail!(\"frame starting from current row cannot have preceding rows\")\n            }\n            _ => {}\n        }\n\n        for bound in [start, end] {\n            if let Some(offset) = bound.offset_value() {\n                offset_checker(offset)?;\n            }\n        }\n","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/core/src/window_function/call.rs#L201-L237","documentation":"`Frame::validate_bounds` checks that a window frame's [start, end) bound pair is logically ordered. An end bound of UNBOUNDED PRECEDING would put the frame end before any possible start, so it is rejected outright.","triggerScenarios":"A window frame is parsed/planned whose end bound resolves to `FrameBound::UnboundedPreceding` — e.g. SQL like `ROWS BETWEEN CURRENT ROW AND UNBOUNDED PRECEDING` reaches validation (usually after binder should have rejected it, or via direct struct/protobuf construction).","commonSituations":"Hand-written SQL with reversed frame bounds relying on the binder to catch it but hitting the executor-side validation; programmatic construction of `Frame` in tests or internal APIs.","solutions":["Fix the SQL so the frame end is not before the start (e.g. `ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW`).","If writing SQL programmatically, swap start/end so the smaller bound is the start.","Ensure the SQL binder rejects reversed frames early with a user-friendly message before reaching this internal validation."],"exampleFix":"-- before\nOVER (ORDER BY ts ROWS BETWEEN CURRENT ROW AND UNBOUNDED PRECEDING)\n-- after\nOVER (ORDER BY ts ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)","handlingStrategy":"validation","validationCode":"// pre-validate before constructing Frame\ndebug_assert!(!matches!(end, FrameBound::UnboundedPreceding), \"end cannot be UNBOUNDED PRECEDING\");","typeGuard":"fn bounds_ordered<T>(s: &FrameBound<T>, e: &FrameBound<T>) -> bool { !matches!(e, FrameBound::UnboundedPreceding) }","tryCatchPattern":"match frame.validate() {\n    Ok(()) => (),\n    Err(e) if e.to_string().contains(\"frame end cannot be UNBOUNDED PRECEDING\") => bail!(\"reversed window frame bounds\"),\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Validate frame bounds in the SQL binder so users get friendly errors early","Never construct Frame manually with reversed bounds in tests/tools","Write SQLLint/unit tests covering all bound pair combinations"],"tags":["window-function","frame-bounds","validation","sql"],"backgroundTag":"invalid-argument-value","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"}