risingwavelabs/risingwave · error
frame starting from following row cannot have preceding rows
Error message
frame starting from following row cannot have preceding rows
What it means
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).
Source
Thrown at src/expr/core/src/window_function/call.rs:224
fn offset_value(&self) -> Option<&T> {
match self {
UnboundedPreceding | UnboundedFollowing | CurrentRow => None,
Preceding(offset) | Following(offset) => Some(offset),
}
}
pub(super) fn validate_bounds(
start: &Self,
end: &Self,
offset_checker: impl Fn(&T) -> Result<()>,
) -> Result<()> {
match (start, end) {
(_, UnboundedPreceding) => bail!("frame end cannot be UNBOUNDED PRECEDING"),
(UnboundedFollowing, _) => {
bail!("frame start cannot be UNBOUNDED FOLLOWING")
}
(Following(_), CurrentRow) | (Following(_), Preceding(_)) => {
bail!("frame starting from following row cannot have preceding rows")
}
(CurrentRow, Preceding(_)) => {
bail!("frame starting from current row cannot have preceding rows")
}
_ => {}
}
for bound in [start, end] {
if let Some(offset) = bound.offset_value() {
offset_checker(offset)?;
}
}
Ok(())
}
pub fn map<U>(self, f: impl Fn(T) -> U) -> FrameBound<U> {
match self {View on GitHub (pinned to 6469eb736d)
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.
Example fix
-- before SELECT sum(x) OVER (ORDER BY ts ROWS BETWEEN 2 FOLLOWING AND CURRENT ROW) FROM t; -- after SELECT sum(x) OVER (ORDER BY ts ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) FROM t;
Defensive patterns
Strategy: validation
Validate before calling
fn frame_bounds_valid(start: &Bound, end: &Bound) -> bool {
!matches!((start, end), (Bound::Following(_), Bound::CurrentRow) | (Bound::Following(_), Bound::Preceding(_)))
} Type guard
fn is_following(b: &Bound) -> bool { matches!(b, Bound::Following(_)) } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- frame end cannot be UNBOUNDED PRECEDING
- frame start cannot be UNBOUNDED FOLLOWING
- frame starting from current row cannot have preceding rows
- Invalid order key: empty item in `{expr}`
- Invalid order key item `{item}`
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/0eeeec04b5714e34.
Report an issue: GitHub.