risingwavelabs/risingwave · error

Type mismatched between then clause and case

Error message

Type mismatched between then clause and case

What it means

RisingWave's CASE expression builder validates that every THEN clause's return type matches the declared CASE result type. If a WHEN...THEN pair produces a value of a different type than the coalesced output type, building the expression fails. This is an upfront type-consistency check so evaluation never hits a mixed-type array.

Source

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

    };
    Ok(ConstantLookupExpression::new(return_type, sync_arms, fallback, operand).boxed())
}

#[build_function("case(...) -> any", type_infer = "unreachable")]
fn build_case_expr(
    return_type: DataType,
    children: Vec<BoxedExpression>,
) -> Result<BoxedExpression> {
    // children: (when, then)+, (else_clause)?
    let len = children.len();
    let mut when_clauses = Vec::with_capacity(len / 2);
    let mut iter = children.into_iter().array_chunks();
    for [when, then] in iter.by_ref() {
        if when.return_type() != DataType::Boolean {
            bail!("Type mismatched between when clause and condition");
        }
        if then.return_type() != return_type {
            bail!("Type mismatched between then clause and case");
        }
        when_clauses.push(WhenClause { when, then });
    }
    let else_clause = 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 sync_when_clauses = match try_convert_all(
        when_clauses,
        |WhenClause { when, then }| match (when, then) {
            (BoxedExpression::Sync(when), BoxedExpression::Sync(then)) => {
                Ok(WhenClause { when, then })
            }

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Cast the offending THEN clause to the CASE result type, e.g. `THEN col::varchar`.
  2. Check each branch's return type before building and unify them to a common type.
  3. If you control query construction, ensure the frontend/planner inserts explicit cast expressions so all branches match.

Example fix

// before
build_case_expr(return_type, vec![when_expr, then_int_expr]) // then returns INT, return_type is VARCHAR
// after
let then_expr = then_int_expr.cast(DataType::Varchar)?;
build_case_expr(return_type, vec![when_expr, then_expr])
Defensive patterns

Strategy: validation

Validate before calling

fn validate_case_branches(return_type: DataType, branches: &[BoxedExpression]) -> Result<()> {
    for then_expr in branches.iter().skip(1).step_by(2) {
        if then_expr.return_type() != return_type {
            return Err(format!("then type {:?} != case type {:?}", then_expr.return_type(), return_type).into());
        }
    }
    Ok(())
}

Type guard

fn is_compatible(e: &BoxedExpression, rt: &DataType) -> bool { e.return_type() == *rt }

Prevention

When it happens

Trigger: Calling build_case_expr with children where a `then` expression's return_type() differs from the computed `return_type` of the CASE expression, e.g. CASE with `THEN 1` branches and an `ELSE 'x'`/varchar-branch that forces the result type to VARCHAR while an INT then-branch remains.

Common situations: Mixing numeric and string branches in CASE; implicit casts not inserted because the frontend planned a different common type; user-defined functions returning unexpected types in one branch.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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