{"record":{"id":"0eeeec04b5714e34","repo":"risingwavelabs/risingwave","slug":"frame-starting-from-following-row-cannot-have-prec","errorCode":null,"errorMessage":"frame starting from following row cannot have preceding rows","messagePattern":"frame starting from following row cannot have preceding rows","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/core/src/window_function/call.rs","lineNumber":224,"sourceCode":"    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\n        Ok(())\n    }\n\n    pub fn map<U>(self, f: impl Fn(T) -> U) -> FrameBound<U> {\n        match self {","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/core/src/window_function/call.rs#L206-L242","documentation":"RisingWave validates window frame bounds (RANGE/ROWS frame start/end) when constructing a WindowFuncCall. A frame whose start bound is a Following row cannot also end before the start; this match arm rejects any frame starting from a FOLLOWING row that has a preceding-row end (CurrentRow or Preceding).","triggerScenarios":"Calling WindowFuncCall construction with frame bounds such as start=Following(n) and end=CurrentRow, or start=Following(n) and end=Preceding(m). The combination is rejected in validate_bounds because the end bound would precede the start bound.","commonSituations":"Hand-written SQL with an OVER clause like `ROWS BETWEEN 2 FOLLOWING AND CURRENT ROW`, or a frontend/planner bug producing reversed frame bounds when translating SQL AST into protobuf WindowFunction messages.","solutions":["Rewrite the frame so the start bound is not after the end bound, e.g. `ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING` or `ROWS BETWEEN 2 PRECEDING AND CURRENT ROW`.","Swap start/end if they were accidentally reversed in the SQL or in the protobuf frame bounds.","Check the frontend planner code that builds PbFrameBound to ensure Following offsets are not paired with preceding ends."],"exampleFix":"-- before\nSELECT sum(x) OVER (ORDER BY ts ROWS BETWEEN 2 FOLLOWING AND CURRENT ROW) FROM t;\n-- after\nSELECT sum(x) OVER (ORDER BY ts ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) FROM t;","handlingStrategy":"validation","validationCode":"fn frame_bounds_valid(start: &Bound, end: &Bound) -> bool {\n    !matches!((start, end), (Bound::Following(_), Bound::CurrentRow) | (Bound::Following(_), Bound::Preceding(_)))\n}","typeGuard":"fn is_following(b: &Bound) -> bool { matches!(b, Bound::Following(_)) }","tryCatchPattern":null,"preventionTips":["Always emit frame bounds with start <= end in SQL time ordering.","Lint user SQL for FOLLOWING-start frames paired with non-following ends.","Test planner-generated frame bounds with property checks on ordering."],"tags":["window-function","frame-bounds","sql","validation"],"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"}