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

  1. Make the child expression type `Sync` (replace `Rc` with `Arc`, remove non-Sync interior mutability)
  2. Check which expression variant is in the IN set and ensure it implements `Sync`/`Send`
  3. 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

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


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)