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

  1. 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`.
  2. Swap start/end if they were accidentally reversed in the SQL or in the protobuf frame bounds.
  3. 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

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


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