pola-rs/polars · error · TypeError

input frames must be of a consistent type (all LazyFrame or

Error message

input frames must be of a consistent type (all LazyFrame or all DataFrame)

What it means

Raised by align_frames when the frames are not all the same concrete type. The check compares {type(f) for f in frames}; any mix — typically pl.DataFrame from read_* combined with pl.LazyFrame from scan_* — raises TypeError. A sole non-frame argument is first unpacked as an iterable of frames.

Source

Thrown at py-polars/src/polars/functions/eager.py:978

    ├╌╌╌╌╌╌╌┤
    │ 167.5 │
    ├╌╌╌╌╌╌╌┤
    │ 47.0  │
    └───────┘
    """  # noqa: W505
    if not frames:
        return []

    if len(frames) == 1 and not isinstance(frames[0], (pl.DataFrame, pl.LazyFrame)):
        frames = frames[0]  # type: ignore[assignment]
    if isinstance(frames, (Generator, Iterator)):
        frames = tuple(frames)

    if len({type(f) for f in frames}) != 1:
        msg = (
            "input frames must be of a consistent type (all LazyFrame or all DataFrame)"
        )
        raise TypeError(msg)

    eager = isinstance(frames[0], pl.DataFrame)
    on = [on] if (isinstance(on, str) or not isinstance(on, Sequence)) else on
    align_on = [(c.meta.output_name() if isinstance(c, pl.Expr) else c) for c in on]

    # create aligned master frame (this is the most expensive part; after
    # we just select out the columns representing the component frames)
    idx_frames = [(idx, frame.lazy()) for idx, frame in enumerate(frames)]  # type: ignore[union-attr]
    alignment_frame = _alignment_join(
        *idx_frames, align_on=align_on, how=how, descending=descending, eager=eager
    )

    # select-out aligned components from the master frame
    aligned_cols = set(alignment_frame.collect_schema())
    aligned_frames = []
    for idx, lf in idx_frames:
        sfx = f":{idx}"
        df_cols = [

View on GitHub (pinned to df599052da)

Solutions

  1. Normalize to one type: apply .lazy() to every frame (returns LazyFrames) or .collect() to every frame (returns DataFrames)
  2. Pick one execution model per pipeline stage and enforce it at the boundary
  3. If only schemas were needed from the lazy frames, use collect_schema() and keep data eager

Example fix

# before
pl.align_frames(df1, lf2, on='ts', how='inner')  # TypeError

# after (all lazy)
pl.align_frames(df1.lazy(), lf2, on='ts', how='inner')
Defensive patterns

Strategy: type-guard

Validate before calling

if len({type(f) for f in frames}) != 1:
    frames = [f.lazy() for f in frames]  # normalize to LazyFrame
aligned = pl.align_frames(*frames, on=on, how=how)

Type guard

def uniform_frames(frames) -> bool:
    return (
        bool(frames)
        and len({type(f) for f in frames}) == 1
        and isinstance(frames[0], (pl.DataFrame, pl.LazyFrame))
    )

Prevention

When it happens

Trigger: pl.align_frames(df1, lf2, on='ts'); a list built partly from read_parquet (eager) and partly from scan_parquet (lazy); a generator mixing collected and scanned frames.

Common situations: Pipelines that scan large files lazily but read small lookup tables eagerly; helpers that collect some frames for size checks and pass others through lazily.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/54986bba5064ae78. Report an issue: GitHub.