Polars py-1.44.1 - when/then/otherwise Correctness


Python Polars py-1.44.1 shipped on 26 August 2026, two days after py-1.44.0. The GitHub body names five contributors and lists no diffs. The tag is a correctness patch for when/then/otherwise, which is why the previous Python wheel was yanked.

The full release notes and downloads are on the GitHub release page.

The first commit on the tag range fixes when/then/otherwise when the predicate carries a null mask that is not a scalar. A later branch was skipped when an earlier predicate was null, including on columns that came from a Parquet scan. The frame collected. The values were wrong. There was no exception.

A second commit on the same expression path fixes broadcasting. Mixed length then and otherwise arms could align to the wrong row. Both land in the Python API as the same when chain operators already use. ETL jobs that map status flags, fill defaults, or branch on nullable keys are the blast radius.

PyPI yanked 1.44.0 with the reason when/then/otherwise regression. That is the operator signal this tag exists. The public GitHub release page still does not mention either fix. Lock files that only bump when the GitHub body changes will skip py-1.44.1.

Recompute outputs that ran when on py-1.44.0. Nullable predicates and mixed length branches are the cases to rerun first. Silent wrong rows beat a crash for pipeline damage.

A second correctness path sits in the Parquet reader. read_parquet and scan_parquet failed on a legal gzip layout: one data page, two gzip members concatenated. The Apache parquet-testing file concatenated_gzip_members.parquet stores 513 UINT64 values this way.

Polars decoded that page with flate2::read::GzDecoder, which stops after the first member. The error was File out of specification: underlying IO error: failed to fill whole buffer. The file was in spec. The decoder was not.

The patch switches that path in polars-parquet to MultiGzDecoder. Single member files stay the same. Concatenated members now decode. CSV already used the multi member decoder. This is a reader compatibility fix, not a new scan option and not a write format change.

Jobs that already read these files on 1.44.0 were failing closed. After the bump they will succeed. Treat that as a data volume change, not as a silent rewrite of rows that used to load.

pl.lit with an explicit pl.Unknown dtype used a different code path than a bare pl.lit. pl.lit(value, pl.Unknown) built a typed Series, so 1 became Int64. Then Float32 / pl.lit(1, pl.Unknown) promoted to Float64 while Float32 / pl.lit(1) stayed Float32. Same literal, two dtypes, two results.

py-1.44.1 makes pl.lit(value, pl.Unknown) the same as pl.lit(value). Casts to Unknown(Any) are dropped during IR conversion instead of sitting as planner noops that the engine treated as clones. Schema and engine stay in agreement by construction.

Python stubs also retagged collect_batches as _CollectBatches instead of Iterator[DataFrame]. That is a typing change. Runtime collect behavior is unchanged. Type checkers that pinned the old iterator alias may need a stub refresh. Query results will not move because of it.

This is a stable point release, not a prerelease. Pin Python Polars to 1.44.1. Leave 1.44.0 out of production locks.

No schema migration is documented. No CLI flag changed. No config key changed. The work is six commits and 20 files over py-1.44.0.

Rerun when pipelines that ran on the yanked wheel. Reread Parquet that previously failed with the gzip buffer error. Recheck arithmetic that passed pl.Unknown into pl.lit. Contributors listed on the release body are Kevin-Patyk, henrytsanford, jonasdedden, orlp, and ritchie46.