risingwavelabs/risingwave · error
IN set expression must be sync
Error message
IN set expression must be sync
What it means
During constant folding of an IN expression, RisingWave evaluates every element of the IN set as a constant so the whole predicate can be reduced. Each set element expression must implement the Sync marker because it is evaluated inside a non-async context (and may be shared across threads); an element that is not `Sync` cannot be safely evaluated here, so the builder bails out instead of risking unsound evaluation.
Solutions
- Make the child expression type `Sync` (replace `Rc` with `Arc`, remove non-Sync interior mutability)
- Check which expression variant is in the IN set and ensure it implements `Sync`/`Send`
- If the element genuinely cannot be sync, avoid putting it in an IN set constant-fold path (keep the generic IN evaluation instead of the specialized builder)
Example fix
// before
struct MyExpr { inner: Rc<RefCell<State>> }
// after
struct MyExpr { inner: Arc<State> } Defensive patterns
Strategy: type-guard
Validate before calling
// assert all IN-set children are Sync at compile time: fn assert_sync<T: Sync>() {} assert_sync::<BoxedExpression>(); Type guard
fn is_sync(e: &BoxedExpression) -> bool { let _: &dyn Sync = unsafe { std::mem::transmute(e) }; true } // prefer compile-time T: Sync bounds Try / catch
match new_in_expr(...) { Err(e) if e.to_string().contains("must be sync") => { /* fall back to generic IN evaluation */ }, r => r? } Prevention
- Use Arc instead of Rc in expression internals
- Run clippy/miri checks for Send/Sync on expression types
- Add compile-time assertions for Sync on all expression variants
When it happens
Trigger: Calling `new_in_expr` (IN expression construction) where one of the child expressions after the first (the left operand) is a `BoxedExpression` that does not implement `Sync`, e.g. a non-thread-safe expression type wrapped in the box.
Common situations: Custom or internal expression implementations that hold `Rc`, raw pointers, or non-Sync references; hitting this after writing a custom scalar expression or when a new expression variant forgets to be thread-safe while being usable as an IN-set constant.
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
- concurrent backup job is not supported: existent job
- Iceberg branch disappeared after manifest rewrite for sink
- manual iceberg compaction is already waiting for sink
- {0}
- {0}
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/f3b001cc5e240be3.
Report an issue: GitHub.
Appendix: source
Thrown at src/expr/impl/src/scalar/in_.rs:120
impl<E: AsyncExpression> AsyncExpression for InExpression<E> {
async fn eval(&self, input: &DataChunk) -> Result<ArrayRef> {
eval_in!(async, self, input)
}
async fn eval_row(&self, input: &OwnedRow) -> Result<Datum> {
eval_row_in!(async, self, input)
}
}
#[build_function("in(any, ...) -> boolean")]
fn build(return_type: DataType, children: Vec<BoxedExpression>) -> Result<BoxedExpression> {
let mut iter = children.into_iter();
let left_expr = iter.next().unwrap();
let mut data = Vec::with_capacity(iter.size_hint().0);
let data_chunk = DataChunk::new_dummy(1);
for child in iter {
let BoxedExpression::Sync(child) = child else {
bail!("IN set expression must be sync")
};
let array = child.eval(&data_chunk)?;
let datum = array.value_at(0).to_owned_datum();
data.push(datum);
}
Ok(match left_expr {
BoxedExpression::Sync(left_expr) => {
InExpression::new(left_expr, data.into_iter(), return_type).boxed()
}
left_expr @ BoxedExpression::Async(_) => {
InExpression::new(left_expr, data.into_iter(), return_type).boxed()
}
})
}
#[cfg(test)]
mod tests {
use risingwave_common::array::DataChunk;View on GitHub (pinned to 6469eb736d)