{"record":{"id":"6efafeef2d059e94","repo":"risingwavelabs/risingwave","slug":"type-mismatched-between-else-and-case","errorCode":null,"errorMessage":"Type mismatched between else and case.","messagePattern":"Type mismatched between else and case\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/expr/impl/src/scalar/case.rs","lineNumber":288,"sourceCode":"\n    let mut children = children;\n\n    let operand = children.remove(0);\n\n    let mut arms = HashMap::new();\n\n    // Build the `arms` with iterating over `when` & `then` clauses\n    let mut iter = children.into_iter().array_chunks();\n    for [when, then] in iter.by_ref() {\n        let Ok(Some(s)) = when.eval_const() else {\n            bail!(\"expect when expression to be const\");\n        };\n        arms.insert(s, then);\n    }\n\n    let fallback = 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 BoxedExpression::Sync(operand) = operand else {\n        return Ok(ConstantLookupExpression::new(return_type, arms, fallback, operand).boxed());\n    };\n    let arms: Vec<_> = arms.into_iter().collect();\n    let sync_arms: HashMap<ScalarImpl, Arc<dyn SyncExpression>> = match try_convert_all(\n        arms,\n        |(key, expr)| match expr {\n            BoxedExpression::Sync(expr) => Ok((key, expr)),\n            expr @ BoxedExpression::Async(_) => Err((key, expr)),\n        },\n        |(key, expr)| (key, BoxedExpression::Sync(expr)),\n    ) {","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/expr/impl/src/scalar/case.rs#L270-L306","documentation":"In `build_constant_lookup_expr`, the optional trailing ELSE clause must have the same return type as the CASE's declared return type. A mismatch would make the output column type inconsistent at runtime, so the builder rejects it during construction.","triggerScenarios":"Building a constant-lookup CASE where the ELSE expression's DataType differs from the CASE return_type (e.g. ELSE returns INT when the CASE was typed as VARCHAR), typically when RETURN_TYPE coercion was skipped or types were declared incorrectly upstream.","commonSituations":"Hand-built expression trees; frontend type-inference bugs where implicit casts to the common type were not inserted; schema changes altering a column type used in ELSE.","solutions":["Add an explicit cast on the ELSE clause to match the CASE return type.","Verify the type inference pass assigned a common return type across all THEN/ELSE arms.","Check for schema drift in the columns referenced by the ELSE expression."],"exampleFix":"// before\nCASE key WHEN 1 THEN 'a' ELSE 42 END  -- else is int, case is varchar\n// after\nCASE key WHEN 1 THEN 'a' ELSE 42::varchar END","handlingStrategy":"validation","validationCode":"-- assert else type matches case return type before build\n-- app-side check:\nif (elseExpr.returnType !== caseReturnType) addCast(elseExpr, caseReturnType);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always cast ELSE to the common CASE type explicitly.","Verify return types of all THEN/ELSE arms when writing SQL.","Watch for schema changes altering a column's type used in ELSE."],"tags":["rust","type-mismatch","expression-builder","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"}