{"record":{"id":"26f42e62664a57ae","repo":"risingwavelabs/risingwave","slug":"range-frame-offset-add-expression-must-be-sync","errorCode":null,"errorMessage":"range frame offset add expression must be sync","messagePattern":"range frame offset add expression must be sync","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/core/src/window_function/range.rs","lineNumber":341,"sourceCode":"            offset,\n            add_expr: None,\n            sub_expr: None,\n        }\n    }\n\n    fn prepare(&mut self, order_data_type: &DataType, offset_data_type: &DataType) -> Result<()> {\n        use risingwave_pb::expr::expr_node::PbType as PbExprType;\n\n        let input_expr = InputRefExpression::new(order_data_type.clone(), 0);\n        let offset_expr =\n            LiteralExpression::new(offset_data_type.clone(), Some(self.offset.clone()));\n        let add_expr = build_func(\n            PbExprType::Add,\n            order_data_type.clone(),\n            vec![input_expr.clone().boxed(), offset_expr.clone().boxed()],\n        )?;\n        let crate::expr::BoxedExpression::Sync(add_expr) = add_expr else {\n            bail!(\"range frame offset add expression must be sync\");\n        };\n        self.add_expr = Some(add_expr);\n\n        let sub_expr = build_func(\n            PbExprType::Subtract,\n            order_data_type.clone(),\n            vec![input_expr.boxed(), offset_expr.boxed()],\n        )?;\n        let crate::expr::BoxedExpression::Sync(sub_expr) = sub_expr else {\n            bail!(\"range frame offset subtract expression must be sync\");\n        };\n        self.sub_expr = Some(sub_expr);\n        Ok(())\n    }\n\n    pub fn new_for_test(\n        offset: ScalarImpl,\n        order_data_type: &DataType,","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/core/src/window_function/range.rs#L323-L359","documentation":"When preparing a RangeFrameBounds, the generated add (and subtract) expression between the order column and offset is built via build_func; the result must be a synchronous (non-async/non-table) expression for the frame calculation to work inline. If build_func returns anything else, from_protobuf fails with this invariant error.","triggerScenarios":"Deserializing a RANGE frame where PbExprType::Add (or Subtract) over the order/offset data types builds into a non-sync expression — typically an unsupported type combination that maps to an async or missing expression implementation.","commonSituations":"Exotic order-column/offset type pairs (e.g. certain decimal/interval/timestamptz combos) where the arithmetic expression engine has no sync implementation; expression framework refactors changing BoxedExpression variants.","solutions":["Use a supported order column type for RANGE frames (integer, numeric, date/timestamp, or compatible interval) so Add/Subtract build as sync expressions.","Add a sync expression implementation for the missing type combination in the expression crate.","Check recent expression-framework changes if this previously worked (BoxedExpression::Sync variant coverage)."],"exampleFix":"// ensure build_func result is sync for the type pair, or add an impl:\n// before: Add on timestamptz + interval-with-months falls to unsupported variant\n// after: restrict offset to microsecond-only intervals so Add maps to BoxedExpression::Sync","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let add_expr = match build_func(PbExprType::Add, order_type.clone(), vec![input.boxed(), offset.boxed()]) {\n    Ok(crate::expr::BoxedExpression::Sync(e)) => e,\n    Ok(_) | Err(e2) => return Err(anyhow!(\"unsupported RANGE frame types (order/offset): {:?}\", e2.map(|e| e.to_string()))),\n};","preventionTips":["Restrict RANGE frames to type pairs with sync Add/Subtract support.","Add expression coverage tests for every supported order/offset type combination.","Fail fast in the frontend binder when the order/offset types lack arithmetic support."],"tags":["window-function","range-frame","expression","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}