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
- 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
- Update `build_func`/the relevant expression constructor so Subtract over this data type builds a Sync expression
- 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
- Keep expression builder constructors returning Sync variants for all arithmetic ops
- Add a unit test asserting Subtract on each supported type builds a Sync expression
- Pin frontend/backend versions in deployment
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
- session frame gap add expression must be sync
- for session gap of type `interval`, each field should be…
- for session order column of type `timestamptz`, gap should…
- offset of `RowsFrameBound` must be `Integer`
- session gap should be non-negative, but
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)