risingwavelabs/risingwave · error

frame starting from current row cannot have preceding rows

Error message

frame starting from current row cannot have preceding rows

What it means

During window frame bound validation, a frame starting at CURRENT ROW is not allowed to end at a Preceding row, since the end would be before the start. validate_bounds rejects this contradictory frame specification.

Source

Thrown at src/expr/core/src/window_function/call.rs:227

            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 {
            UnboundedPreceding => UnboundedPreceding,
            Preceding(offset) => Preceding(f(offset)),
            CurrentRow => CurrentRow,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Rewrite the frame as `BETWEEN n PRECEDING AND CURRENT ROW` to scan backwards.
  2. Use `BETWEEN CURRENT ROW AND n FOLLOWING` if forward-looking rows were intended.
  3. Verify planner/frontend code that emits PbFrameBound pairs does not reverse start/end.

Example fix

-- before
SELECT sum(x) OVER (ORDER BY ts ROWS BETWEEN CURRENT ROW AND 3 PRECEDING) FROM t;
-- after
SELECT sum(x) OVER (ORDER BY ts ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) FROM t;
Defensive patterns

Strategy: validation

Validate before calling

fn frame_bounds_valid(start: &Bound, end: &Bound) -> bool {
    !matches!((start, end), (Bound::CurrentRow, Bound::Preceding(_)))
}

Type guard

fn is_current_row_start(b: &Bound) -> bool { matches!(b, Bound::CurrentRow) }

Prevention

When it happens

Trigger: Constructing a WindowFuncCall with frame bounds start=CurrentRow and end=Preceding(n), e.g. SQL `ROWS BETWEEN CURRENT ROW AND 3 PRECEDING`.

Common situations: User SQL with reversed frame direction (author intended FOLLOWING), or programmatic frame construction that mixes up preceding/following semantics.

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/380503b1d6ffd567. Report an issue: GitHub.