{"record":{"id":"6cf51e7a7b87d0b6","repo":"pola-rs/polars","slug":"expr-expected-did-you-call-when-then-otherwis","errorCode":null,"errorMessage":"expr expected, did you call when().then().otherwise?","messagePattern":"expr expected, did you call when\\(\\)\\.then\\(\\)\\.otherwise\\?","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/polars-plan/src/dsl/arity.rs","lineNumber":129,"sourceCode":"        //             )                                         |       |\n        //         )                                            _|      _|\n        //\n        // by iterating LIFO we first create\n        // `inner` and then assign that to `otherwise`,\n        // which will be used in the next layer `outer`\n        //\n\n        let conditions_iter = self.conditions.into_iter().rev();\n        let mut statements_iter = self.statements.into_iter().rev();\n\n        let mut otherwise = expr.into();\n\n        for e in conditions_iter {\n            otherwise = ternary_expr(\n                e,\n                statements_iter\n                    .next()\n                    .expect(\"expr expected, did you call when().then().otherwise?\"),\n                otherwise,\n            );\n        }\n\n        otherwise\n    }\n}\n\n/// Start a `when-then-otherwise` expression.\npub fn when<E: Into<Expr>>(condition: E) -> When {\n    When {\n        condition: condition.into(),\n    }\n}\n\npub fn ternary_expr(predicate: Expr, truthy: Expr, falsy: Expr) -> Expr {\n    Expr::Ternary {\n        predicate: Arc::new(predicate),","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/crates/polars-plan/src/dsl/arity.rs#L111-L147","documentation":"ChainedThen::otherwise() rebuilds a when/then/otherwise chain by iterating conditions and statements in reverse and nesting ternary expressions. The invariant is one then() per when(), so statements_iter.next() always yields; if a ChainedThen was built with more conditions than statements (only possible by constructing the structs manually or via corrupted deserialization), next() returns None and this expect panics.","triggerScenarios":"Constructing ChainedWhen/ChainedThen structs directly with mismatched conditions/statements vectors, or deserializing a hand-crafted/older-format expression where the pairing is broken. The fluent API (when().then().when().then().otherwise()) cannot produce it.","commonSituations":"Code that builds DSL structs manually instead of the fluent API; expression round-trips between polars versions with incompatible internal serialization.","solutions":["Use the fluent API: every when(cond) must be followed by then(expr) before otherwise(...)","Do not construct ChainedWhen/ChainedThen literally; keep conditions.len() == statements.len() if you must","Serialize and deserialize expressions with the same polars version on both ends"],"exampleFix":"// before (manual construction breaks the invariant)\nlet e = ChainedThen { conditions: vec![c1, c2], statements: vec![s1] }.otherwise(def);\n\n// after: fluent API keeps pairing correct\nlet e = when(c1).then(s1).when(c2).then(s2).otherwise(def);","handlingStrategy":"validation","validationCode":"// before calling otherwise(), assert the builder invariant\nassert_eq!(chained.conditions.len(), chained.statements.len(), \"each when() needs a then()\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use the fluent when().then() API exclusively","Never construct ChainedWhen/ChainedThen structs manually","Round-trip expressions only between identical polars versions"],"tags":["dsl","expression","when-then-otherwise","api-misuse","panic","rust"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}