risingwavelabs/risingwave · error

the window frame must be valid

Error message

the window frame must be valid

What it means

When constructing the window aggregate state, RisingWave validates the frame bounds with `bounds.validate()`. If the bounds are invalid (e.g. malformed UNBOUNDED/offset combinations), construction is aborted because the aggregate would produce undefined results while sliding the frame.

Solutions

  1. Fix the frontend/planner so only valid frames reach the executor
  2. Correct the OVER clause frame specification in the query
  3. Check `WindowFrameBounds::validate` logic if a legitimate frame is being rejected

Example fix

// before
bail!("the window frame must be valid");
// after
bail!("invalid window frame bounds: {:?}", call.frame.bounds); // at minimum log which bounds failed
Defensive patterns

Strategy: validation

Validate before calling

fn frame_is_valid(bounds: &WindowFrameBounds) -> bool { bounds.validate().is_ok() }

Try / catch

match result {
    Err(e) if e.to_string().contains("window frame must be valid") => fix_or_reject_frame(query),
    other => other?,
}

Prevention

When it happens

Trigger: Creating an aggregate window function (WindowFuncKind::Aggregate) whose WindowFrameBounds fail `validate()` — e.g. a frame with invalid start/end ordering or an offset frame that is not permitted.

Common situations: A planner/frontend bug letting a malformed OVER clause through; manual construction of WindowFuncCall in tests; a frame like ROWS BETWEEN 2 FOLLOWING AND 1 PRECEDING.

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

Appendix: source

Thrown at src/expr/impl/src/window_function/aggregate.rs:53

use super::buffer::{RangeWindow, RowsWindow, SessionWindow, WindowBuffer, WindowImpl};

type StateValue = SmallVec<[Datum; 2]>;

struct AggregateState<W>
where
    W: WindowImpl<Key = StateKey, Value = StateValue>,
{
    agg_impl: AggImpl,
    arg_data_types: Vec<DataType>,
    ignore_nulls: bool,
    buffer: WindowBuffer<W>,
    buffer_heap_size: KvSize,
}

pub(super) fn new(call: &WindowFuncCall) -> Result<BoxedWindowState> {
    if call.frame.bounds.validate().is_err() {
        bail!("the window frame must be valid");
    }
    let agg_type = must_match!(&call.kind, WindowFuncKind::Aggregate(agg_type) => agg_type);
    let arg_data_types = call.args.arg_types().to_vec();
    let agg_call = AggCall {
        agg_type: agg_type.clone(),
        args: call.args.clone(),
        return_type: call.return_type.clone(),
        column_orders: Vec::new(), // the input is already sorted
        // TODO(rc): support filter on window function call
        filter: None,
        // TODO(rc): support distinct on window function call? PG doesn't support it either.
        distinct: false,
        direct_args: vec![],
    };

    let (agg_impl, enable_delta) = match agg_type {
        AggType::Builtin(PbAggKind::FirstValue) => (AggImpl::Shortcut(Shortcut::FirstValue), false),
        AggType::Builtin(PbAggKind::LastValue) => (AggImpl::Shortcut(Shortcut::LastValue), false),

View on GitHub (pinned to 6469eb736d)