risingwavelabs/risingwave · error

expect when expression to be const

Error message

expect when expression to be const

What it means

The constant-lookup optimization requires each WHEN key to be a constant that can be evaluated at build time so it can be stored in a HashMap. If `eval_const` on a WHEN expression fails or returns NULL, the builder cannot build the lookup table and bails.

Solutions

  1. Ensure WHEN keys are literal constants in the query.
  2. Check whether the planner incorrectly converted a general CASE to constant_lookup; report/fix the frontend rule.
  3. Use the general `build_case_expr` path instead for non-constant WHEN conditions.

Example fix

// before (non-const WHEN key)
CASE x WHEN some_column THEN 'a' ELSE 'b' END
// after
CASE x WHEN 1 THEN 'a' WHEN 2 THEN 'b' ELSE 'b' END
Defensive patterns

Strategy: validation

Validate before calling

// ensure WHEN is constant before routing to constant lookup
let is_const = matches!(when_expr, Expr::Literal(_) | Expr::InputRef(_)) && when_expr.eval_const().is_ok();
if !is_const { /* use general case builder */ }

Prevention

When it happens

Trigger: `build_constant_lookup_expr` receiving a WHEN expression that is not a literal/stable constant (e.g. a column reference, non-deterministic function) or evaluates to NULL, usually from a planner rule that misjudged const-ness.

Common situations: CASE with non-literal WHEN conditions reaching this builder; planner version differences changing which CASEs are routed to constant lookup.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/expr/impl/src/scalar/case.rs:281

fn build_constant_lookup_expr(
    return_type: DataType,
    children: Vec<BoxedExpression>,
) -> Result<BoxedExpression> {
    if children.is_empty() {
        bail!("children expression must not be empty for constant lookup expression");
    }

    let mut children = children;

    let operand = children.remove(0);

    let mut arms = HashMap::new();

    // Build the `arms` with iterating over `when` & `then` clauses
    let mut iter = children.into_iter().array_chunks();
    for [when, then] in iter.by_ref() {
        let Ok(Some(s)) = when.eval_const() else {
            bail!("expect when expression to be const");
        };
        arms.insert(s, then);
    }

    let fallback = if let Some(else_clause) = iter.into_remainder().next() {
        if else_clause.return_type() != return_type {
            bail!("Type mismatched between else and case.");
        }
        Some(else_clause)
    } else {
        None
    };

    let BoxedExpression::Sync(operand) = operand else {
        return Ok(ConstantLookupExpression::new(return_type, arms, fallback, operand).boxed());
    };
    let arms: Vec<_> = arms.into_iter().collect();
    let sync_arms: HashMap<ScalarImpl, Arc<dyn SyncExpression>> = match try_convert_all(

View on GitHub (pinned to 6469eb736d)