pola-rs/polars · error · ValueError

cross join should not pass join keys

Error message

cross join should not pass join keys

What it means

A cross join (how='cross') produces the Cartesian product of both frames and by definition takes no join keys. Passing `on`, `left_on`, or `right_on` to a cross join is contradictory, so a ValueError is raised.

Solutions

  1. Remove the on/left_on/right_on arguments when using how='cross'
  2. If you actually need keyed matching, change `how` to 'inner', 'left', or 'full' and keep the keys
  3. Filter the cross result with a predicate expression if you need conditional matching

Example fix

// before
df.join(other, on='id', how='cross')
// after
df.join(other, how='cross')
Defensive patterns

Strategy: validation

Validate before calling

def check_cross_keys(how, on, left_on, right_on):
    if how == 'cross' and any(k is not None for k in (on, left_on, right_on)):
        raise ValueError('cross join takes no join keys')

Type guard

def join_args_consistent(how, on, left_on, right_on) -> bool:
    return how != 'cross' or (on is None and left_on is None and right_on is None)

Try / catch

try:
    out = df.join(other, how=how, **keys_kw)
except ValueError as e:
    if 'cross join should not pass join keys' in str(e):
        keys_kw = {}
        out = df.join(other, how='cross')
    else:
        raise

Prevention

When it happens

Trigger: Calling lf.join(other, how='cross', on='id') or with left_on/right_on set; typically from templated join code that always passes key columns regardless of strategy.

Common situations: Parameterized join helpers where `how` is dynamic but keys are hardcoded; intending a regular join but mistakenly setting how='cross'; refactoring an inner/outer join into a cross join without removing keys.

Related errors


AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18). Data as JSON: /api/errors/aa36a9fc0121c083. Report an issue: GitHub.

Appendix: source

Thrown at py-polars/src/polars/lazyframe/frame.py:6236

            maintain_order = "none"

        uses_on = on is not None
        uses_left_on = left_on is not None
        uses_right_on = right_on is not None
        uses_lr_on = uses_left_on or uses_right_on
        if uses_on and uses_lr_on:
            msg = "cannot use 'on' in conjunction with 'left_on' or 'right_on'"
            raise ValueError(msg)
        elif uses_left_on != uses_right_on:
            msg = "'left_on' requires corresponding 'right_on'"
            raise ValueError(msg)

        if how == "outer":  # type: ignore[comparison-overlap]
            msg = "use of `how='outer'` should be replaced with `how='full'`."
            raise ValueError(msg)
        elif how == "outer_coalesce":  # type: ignore[comparison-overlap]
            msg = "use of `how='outer_coalesce'` should be replaced with `how='full', coalesce=True`."
            raise ValueError(msg)
        elif how == "cross":
            if uses_on or uses_lr_on:
                msg = "cross join should not pass join keys"
                raise ValueError(msg)
            return self._from_pyldf(
                self._ldf.join(
                    other._ldf,
                    [],
                    [],
                    allow_parallel,
                    force_parallel,
                    nulls_equal,
                    how,
                    suffix,
                    validate,
                    maintain_order,
                    build_side=build_side,
                    coalesce=None,

View on GitHub (pinned to fe841f959e)