risingwavelabs/risingwave · error

range frame offset subtract expression must be sync

Error message

range frame offset subtract expression must be sync

What it means

When building a RANGE window frame, the framework constructs a Subtract expression used to compute `order_col - offset` for each row. The window function executor drives this expression from its sync (synchronous) evaluation path, so only sync expressions are acceptable. If the built Subtract expression is not of the `BoxedExpression::Sync` variant, construction fails.

Solutions

  1. Check which expression type the frontend emits for the RANGE frame offset Subtract and ensure its Rust implementation exposes a sync `eval` returning `BoxedExpression::Sync` from the builder
  2. Update `build_func`/the relevant expression constructor so Subtract over this data type builds a Sync expression
  3. Verify frontend and backend versions match so protobuf expression nodes are understood by the builder

Example fix

// before: expression built without sync variant
let sub_expr = build_func(PbExprType::Subtract, order_data_type, vec![input_expr.boxed(), offset_expr.boxed()])?;
// after: ensure the constructor returns BoxedExpression::Sync
let crate::expr::BoxedExpression::Sync(sub_expr) = sub_expr else {
    bail!("range frame offset subtract expression must be sync");
};
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: check sync variant before use
fn ensure_sync(e: &BoxedExpression) -> Result<()> {
    if !matches!(e, crate::expr::BoxedExpression::Sync(_)) {
        bail!("expression is not sync");
    }
    Ok(())
}

Type guard

fn is_sync(e: &BoxedExpression) -> bool { matches!(e, BoxedExpression::Sync(_)) }

Try / catch

match build_range_frame(...) {
    Ok(f) => f,
    Err(e) if e.to_string().contains("must be sync") => fallback_to_nonsync_path_or_report(e),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Deserializing a RANGE frame boundary from protobuf (via `from_protobuf`) where the offset expression compiled by the frontend to a Subtract that is async/non-sync, e.g. involving an expression type that only has an async evaluation implementation.

Common situations: Frontend/backend expression-kind mismatch after adding a new expression type without a sync eval implementation; protobuf produced by a newer frontend with expression kinds the older backend builder wraps in a non-sync variant.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

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

        let offset_expr =
            LiteralExpression::new(offset_data_type.clone(), Some(self.offset.clone()));
        let add_expr = build_func(
            PbExprType::Add,
            order_data_type.clone(),
            vec![input_expr.clone().boxed(), offset_expr.clone().boxed()],
        )?;
        let crate::expr::BoxedExpression::Sync(add_expr) = add_expr else {
            bail!("range frame offset add expression must be sync");
        };
        self.add_expr = Some(add_expr);

        let sub_expr = build_func(
            PbExprType::Subtract,
            order_data_type.clone(),
            vec![input_expr.boxed(), offset_expr.boxed()],
        )?;
        let crate::expr::BoxedExpression::Sync(sub_expr) = sub_expr else {
            bail!("range frame offset subtract expression must be sync");
        };
        self.sub_expr = Some(sub_expr);
        Ok(())
    }

    pub fn new_for_test(
        offset: ScalarImpl,
        order_data_type: &DataType,
        offset_data_type: &DataType,
    ) -> Self {
        let mut offset = Self::new(offset);
        offset.prepare(order_data_type, offset_data_type).unwrap();
        offset
    }
}

impl Deref for RangeFrameOffset {
    type Target = ScalarImpl;

View on GitHub (pinned to 6469eb736d)