pola-rs/polars · error
expr expected, did you call when().then().otherwise?
Error message
expr expected, did you call when().then().otherwise?
What it means
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.
Source
Thrown at crates/polars-plan/src/dsl/arity.rs:129
// ) | |
// ) _| _|
//
// by iterating LIFO we first create
// `inner` and then assign that to `otherwise`,
// which will be used in the next layer `outer`
//
let conditions_iter = self.conditions.into_iter().rev();
let mut statements_iter = self.statements.into_iter().rev();
let mut otherwise = expr.into();
for e in conditions_iter {
otherwise = ternary_expr(
e,
statements_iter
.next()
.expect("expr expected, did you call when().then().otherwise?"),
otherwise,
);
}
otherwise
}
}
/// Start a `when-then-otherwise` expression.
pub fn when<E: Into<Expr>>(condition: E) -> When {
When {
condition: condition.into(),
}
}
pub fn ternary_expr(predicate: Expr, truthy: Expr, falsy: Expr) -> Expr {
Expr::Ternary {
predicate: Arc::new(predicate),View on GitHub (pinned to df599052da)
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
Example fix
// before (manual construction breaks the invariant)
let e = ChainedThen { conditions: vec![c1, c2], statements: vec![s1] }.otherwise(def);
// after: fluent API keeps pairing correct
let e = when(c1).then(s1).when(c2).then(s2).otherwise(def); Defensive patterns
Strategy: validation
Validate before calling
// before calling otherwise(), assert the builder invariant assert_eq!(chained.conditions.len(), chained.statements.len(), "each when() needs a then()");
Prevention
- Use the fluent when().then() API exclusively
- Never construct ChainedWhen/ChainedThen structs manually
- Round-trip expressions only between identical polars versions
When it happens
Trigger: 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.
Common situations: Code that builds DSL structs manually instead of the fluent API; expression round-trips between polars versions with incompatible internal serialization.
Related errors
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/6cf51e7a7b87d0b6.
Report an issue: GitHub.