risingwavelabs/risingwave · error

frame bound offset should be non-negative, but

Error message

frame bound offset should be non-negative, but {} is given

What it means

RANGE frame bound offsets must be non-negative. validate_non_negative checks the offset scalar (int, float, decimal) via IsNegative and rejects negative values, since a negative offset would invert frame semantics.

Solutions

  1. Use a non-negative offset and express direction with PRECEDING/FOLLOWING keywords, e.g. `RANGE BETWEEN 1 PRECEDING AND 5 PRECEDING` instead of negative numbers.
  2. Validate offset literals in the frontend before building frame bounds.
  3. If the offset is computed, clamp it to >= 0 at plan time.

Example fix

-- before
SELECT sum(x) OVER (ORDER BY ts RANGE BETWEEN -1 PRECEDING AND 5 PRECEDING) FROM t;
-- after
SELECT sum(x) OVER (ORDER BY ts RANGE BETWEEN 1 PRECEDING AND 5 PRECEDING) FROM t;
Defensive patterns

Strategy: validation

Validate before calling

fn offset_non_negative(v: i64) -> bool { v >= 0 }

Type guard

fn is_valid_offset(d: &Datum) -> bool {
    match d {
        Some(ScalarImpl::Int64(v)) => *v >= 0,
        Some(ScalarImpl::Interval(i)) => i.is_never_negative(),
        _ => false,
    }
}

Prevention

When it happens

Trigger: Creating a RangeFrameBounds with an offset like -5 for `RANGE BETWEEN -5 PRECEDING ...` or a negative interval offset, then calling validate (via from_protobuf/construction).

Common situations: User SQL like `RANGE BETWEEN 5 PRECEDING AND -1 PRECEDING`, or planner emitting a negative offset after constant folding.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at src/expr/core/src/window_function/range.rs:99

}

impl Display for RangeFrameBounds {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "RANGE BETWEEN {} AND {}",
            self.start.for_display(),
            self.end.for_display()
        )?;
        Ok(())
    }
}

impl FrameBoundsImpl for RangeFrameBounds {
    fn validate(&self) -> Result<()> {
        fn validate_non_negative(val: impl IsNegative + Display) -> Result<()> {
            if val.is_negative() {
                bail!(
                    "frame bound offset should be non-negative, but {} is given",
                    val
                );
            }
            Ok(())
        }

        FrameBound::validate_bounds(&self.start, &self.end, |offset| {
            match offset.as_scalar_ref_impl() {
                // TODO(rc): use decl macro?
                ScalarRefImpl::Int16(val) => validate_non_negative(val)?,
                ScalarRefImpl::Int32(val) => validate_non_negative(val)?,
                ScalarRefImpl::Int64(val) => validate_non_negative(val)?,
                ScalarRefImpl::Float32(val) => validate_non_negative(val)?,
                ScalarRefImpl::Float64(val) => validate_non_negative(val)?,
                ScalarRefImpl::Decimal(val) => validate_non_negative(val)?,
                ScalarRefImpl::Interval(val) => {
                    if !val.is_never_negative() {

View on GitHub (pinned to 6469eb736d)