pola-rs/polars · error
'join_where' requires at least one predicate
Error message
'join_where' requires at least one predicate
What it means
polars raises this while converting a join_where (non-equi join) plan from DSL to IR: for left/right joins the engine must fold every predicate into a single AND-ed condition attached to the join node, and it does that with reduce().expect(...), which panics when the predicate list is empty (join.rs:544-554). It means join_where was called with zero predicates — the Python wrapper (LazyFrame.join_where at frame.py:6635) does not check emptiness because predicates are variadic, so the empty list reaches the Rust side unchecked. Note the asymmetry: an inner join_where with zero predicates silently degenerates to a cross join (inner lowers to cross + per-predicate filters), so this panic is specific to how='left'/'right'. In Python it surfaces as polars.exceptions.PanicException at plan-resolution time (collect/explain), not at the call site.
Source
Thrown at crates/polars-plan/src/plans/conversion/dsl_to_ir/join.rs:557
last_node = ctxt.lp_arena.add(ir);
}
} else {
// For left and right joins, we cannot lower to cross + filters
// as null outputs for missing rows would not be preserved.
// We attach the join predicates/conditions to the joins itself
// and restore the original `how` join type.
let node = resolved
.iter()
.map(|e| e.node())
.reduce(|left, right| {
ctxt.expr_arena.add(AExpr::BinaryExpr {
left,
op: Operator::And,
right,
})
})
.expect("'join_where' requires at least one predicate");
let predicate = ExprIR::from_node(node, ctxt.expr_arena);
let IR::Join { options, .. } = ctxt.lp_arena.get(join_node) else {
unreachable!()
};
let mut new_options = (**options).clone();
new_options.args.how = how;
new_options.options = JoinTypeOptionsIR::CrossAndFilter { predicate };
let IR::Join { options, .. } = ctxt.lp_arena.get_mut(join_node) else {
unreachable!()
};
*options = Arc::new(new_options);
}
ctxt.conversion_optimizer
.optimize_exprs(ctxt.expr_arena, ctxt.lp_arena, last_node, false)
.context("'join_where' failed")?;View on GitHub (pinned to 68506541d2)
Solutions
- Pass at least one boolean predicate over columns of both frames, e.g. pl.col('left_a') > pl.col('right_b').
- If predicates are built dynamically, guard the call — raise, skip, or substitute a default predicate when the list is empty.
- If you actually want all row combinations, use lf.join(other, how='cross') instead of join_where.
- If how='left'/'right' was set by mistake, drop it — plain join_where defaults to an inner non-equi join and needs a condition regardless.
Example fix
# before — panics with "'join_where' requires at least one predicate" when preds is empty
result = lf.join_where(other, *preds)
# after — fail fast with a clear Python-side error
if not preds:
msg = 'join_where produced no predicates'
raise ValueError(msg)
result = lf.join_where(other, *preds) Defensive patterns
Strategy: validation
Validate before calling
import polars as pl
def join_where_safe(lf: pl.LazyFrame, other: pl.LazyFrame, predicates: list[pl.Expr]) -> pl.LazyFrame:
if not predicates:
msg = "'join_where' requires at least one predicate"
raise ValueError(msg)
return lf.join_where(other, *predicates) Try / catch
from polars.exceptions import PanicException, InvalidOperationError
try:
out = lf.join_where(other, pl.col("a") > pl.col("b")).collect()
except (PanicException, InvalidOperationError) as exc:
# plan-time failure: audit predicate list and dtypes, then re-issue
... Prevention
- Never splat an unchecked list into join_where; assert it is non-empty first.
- Expect join_where errors to surface at collect()/explain() time, not at the call site — validate early instead of catching late.
- Use join(how='cross') for unconditional joins; join_where exists only for non-equi predicates.
- Keep each predicate a boolean expression over columns of the two frames; combine ORs inside one expression.
When it happens
Trigger: Calling lf.join_where(other) with no predicate expressions; splatting a possibly-empty list, lf.join_where(other, *preds) where preds == []; passing a single empty iterable as the 'predicate' (parse_into_list_of_expressions flattens it to zero exprs); any of the above combined with how='left' or how='right' (the inner path never reaches this expect).
Common situations: Config- or user-driven predicate lists that filter down to empty before the call; refactors that move conditions out of join_where but keep the call site; intending an unconditional/cartesian join and reaching for join_where instead of join(how='cross'); tests with placeholder predicate lists that ship empty.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- cross join filter holds no keys
- use of `how='outer_coalesce'` should be replaced with `how='
- activate 'dtype-array'
- list.to_struct() got a str instead of a list. hint: pass ['{
- schema_mode='overwrite' requires mode='overwrite'
AI-assisted analysis of pola-rs/polars@68506541d2 (2026-08-23).
Data as JSON: /api/errors/1bd8eef33242889d.
Report an issue: GitHub.