{"record":{"id":"188181d08142dc3d","repo":"risingwavelabs/risingwave","slug":"type-mismatched-between-then-clause-and-case","errorCode":null,"errorMessage":"Type mismatched between then clause and case","messagePattern":"Type mismatched between then clause and case","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/impl/src/scalar/case.rs","lineNumber":352,"sourceCode":"    };\n    Ok(ConstantLookupExpression::new(return_type, sync_arms, fallback, operand).boxed())\n}\n\n#[build_function(\"case(...) -> any\", type_infer = \"unreachable\")]\nfn build_case_expr(\n    return_type: DataType,\n    children: Vec<BoxedExpression>,\n) -> Result<BoxedExpression> {\n    // children: (when, then)+, (else_clause)?\n    let len = children.len();\n    let mut when_clauses = Vec::with_capacity(len / 2);\n    let mut iter = children.into_iter().array_chunks();\n    for [when, then] in iter.by_ref() {\n        if when.return_type() != DataType::Boolean {\n            bail!(\"Type mismatched between when clause and condition\");\n        }\n        if then.return_type() != return_type {\n            bail!(\"Type mismatched between then clause and case\");\n        }\n        when_clauses.push(WhenClause { when, then });\n    }\n    let else_clause = if let Some(else_clause) = iter.into_remainder().next() {\n        if else_clause.return_type() != return_type {\n            bail!(\"Type mismatched between else and case.\");\n        }\n        Some(else_clause)\n    } else {\n        None\n    };\n\n    let sync_when_clauses = match try_convert_all(\n        when_clauses,\n        |WhenClause { when, then }| match (when, then) {\n            (BoxedExpression::Sync(when), BoxedExpression::Sync(then)) => {\n                Ok(WhenClause { when, then })\n            }","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/impl/src/scalar/case.rs#L334-L370","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Cast the offending THEN clause to the CASE result type, e.g. `THEN col::varchar`.","Check each branch's return type before building and unify them to a common type.","If you control query construction, ensure the frontend/planner inserts explicit cast expressions so all branches match."],"exampleFix":"// before\nbuild_case_expr(return_type, vec![when_expr, then_int_expr]) // then returns INT, return_type is VARCHAR\n// after\nlet then_expr = then_int_expr.cast(DataType::Varchar)?;\nbuild_case_expr(return_type, vec![when_expr, then_expr])","handlingStrategy":"validation","validationCode":"fn validate_case_branches(return_type: DataType, branches: &[BoxedExpression]) -> Result<()> {\n    for then_expr in branches.iter().skip(1).step_by(2) {\n        if then_expr.return_type() != return_type {\n            return Err(format!(\"then type {:?} != case type {:?}\", then_expr.return_type(), return_type).into());\n        }\n    }\n    Ok(())\n}","typeGuard":"fn is_compatible(e: &BoxedExpression, rt: &DataType) -> bool { e.return_type() == *rt }","tryCatchPattern":null,"preventionTips":["Cast every THEN branch to the intended common type explicitly.","Unify branch types at query-writing time instead of relying on implicit coercion.","Add a pre-build type check for CASE children in code that constructs expressions programmatically."],"tags":["expression","type-mismatch","sql"],"backgroundTag":"type-mismatch","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}