Polars DataFrame Filter: The Always-True (No-Op) Predicate
Searching for a "Polars DataFrame filter always true predicate" usually means one of two things: you want a filter that keeps every row, or you're building predicates dynamically and need a neutral starting point. Here's the thing to clear up first — Polars has no dedicated "always-true" filter function. There's no pl.true(), no always_true flag, no special sentinel. The idiomatic no-op predicate is simply pl.lit(True). This guide shows the correct syntax, why you'd want it, the difference between pl.lit(True) and a bare Python True, and a few historical edge cases that used to bite people.
TL;DR: To filter a Polars DataFrame with an always-true predicate (keep all rows), use
df.filter(pl.lit(True)). A baredf.filter(True)also works on current versions. There is no dedicated always-true function —pl.lit(True)is the idiom.
All code below was verified on Polars 1.42.1 (the latest release as of 2026-07-14; verify the current version on PyPI).
The correct syntax for an always-true filter
The filter method keeps rows where the predicate evaluates to True and discards everything else, including nulls. To keep every row, hand it a predicate that is True for all of them:
import polars as pl
df = pl.DataFrame({"foo": [1, 2, 3], "bar": ["a", "b", "c"]}) df.filter(pl.lit(True)) # returns all 3 rows — a no-op filterpl.lit(True) builds a literal boolean expression. Polars broadcasts that single True across the DataFrame's length, so every row passes. The same idiom works on a LazyFrame:
df.lazy().filter(pl.lit(True)).collect() # all rowsThat's the whole answer. But the reason people search for this is more interesting than the syntax.
Why use an always-true predicate at all?
An always-true predicate is the identity element for logical AND. If you're assembling a filter programmatically — say, from a list of optional conditions supplied by a user or config — you need a neutral value to start from and combine onto. True is that value:
predicate = pl.lit(True) # neutral starting point conditions = [pl.col("foo") > 1] # could be empty, could have many for cond in conditions: predicate = predicate & cond # AND them together df.filter(predicate) # 2 rowsIf conditions is empty, the loop never runs and predicate stays pl.lit(True), so filter correctly returns the whole DataFrame instead of raising or filtering to nothing. Without a neutral start, you'd have to special-case the "no conditions" branch.
The mirror image is the always-false predicate, pl.lit(False), which is the identity element for logical OR. Start from pl.lit(False) when you're OR-ing conditions together and want "no conditions" to mean "no rows."
pl.lit(True) vs a bare Python True
You may have seen both forms floating around. Here's how they compare:
df.filter(pl.lit(True))— explicit literal expression. Recommended.df.filter(True)— Polars accepts a bare Pythonbooland wraps it as a literal internally. Works on current versions.
Both return all rows in a simple no-op. So why prefer pl.lit(True)?
- Clarity of intent.
pl.lit(True)reads as "a literal expression," which is what filter expects. - Safety when combining. When you combine a predicate with
&or|, wrapping the boolean inpl.lit()keeps you firmly in Polars' expression semantics rather than relying on Python's operator resolution. This matters more in older Polars versions, where combining a bareTruewith|produced surprising results (see the edge cases below).
A quick contrast:
# always-true OR'd with a condition = still everyonedf.filter(pl.lit(True) | (pl.col("foo") == 1)) # 3 rows # always-false AND'd with a condition = nobody df.filter(pl.lit(False) & (pl.col("foo") == 1)) # 0 rows
What about filtering a Series?
The pl.lit(True) trick is a DataFrame/LazyFrame idiom. Series.filter expects an actual boolean mask of matching length, not a scalar literal expression. Historically, series.filter(pl.lit(True)) raised an AttributeError because a Series doesn't evaluate expressions the way a frame does. If you're at the Series level and want a no-op, build a full-length boolean mask instead:
s = pl.Series([1, 2, 3]) s.filter(pl.Series([True, True, True])) # keeps all threeIn practice, if you're reaching for an always-true predicate you're almost always working with a frame, where pl.lit(True) is correct.
Historical edge cases worth knowing
A no-op filter sounds like it should never misbehave, but two now-fixed bugs are worth knowing if you're pinned to an older Polars version.
Rowless DataFrame added a phantom row
Between versions 0.19.19 and 0.20.0, filtering a rowless (zero-row) DataFrame with pl.lit(True) incorrectly produced a single row of nulls. This was reported as issue #16664 and subsequently fixed. On 1.42.1 the behavior is correct — a rowless frame stays rowless:
pl.DataFrame({"x": []}).filter(pl.lit(True)).shape # (0, 1) ✓All-null column raised a ShapeError
In version 1.13.0, df.filter(pl.lit(True)) raised ShapeError: filter's length: 1 differs from that of the series: 2 when the DataFrame contained an all-null column (issue #19771). The literal wasn't being broadcast to the frame's length in that specific case. This was fixed by the maintainers, and on 1.42.1 it works as expected:
pl.DataFrame({"a": [None, None]}).filter(pl.lit(True)).height # 2 ✓Takeaway: these were genuine regressions, not intended behavior. If you hit odd results from a no-op filter, check your Polars version first and upgrade — the current release handles empty frames, all-null columns, and combined predicates correctly.
Does the query optimizer strip out an always-true filter?
In the lazy API, Polars runs optimization passes over your query plan. A constant pl.lit(True) predicate is a prime candidate for simplification, and recent releases have expanded predicate handling — Polars 1.42 added optimizer elimination of contradictory filter predicates, per the Polars blog. The exact set of simplifications applied to a literal-true predicate can change release to release. [VERIFY] — if you depend on the optimizer removing a no-op filter for performance, confirm it against df.lazy().filter(pl.lit(True)).explain() on your specific version rather than assuming.
Quick reference
| Goal | Expression | Result |
|---|---|---|
| Keep every row (no-op) | df.filter(pl.lit(True)) | all rows |
| Keep every row (shorthand) | df.filter(True) | all rows |
| Drop every row | df.filter(pl.lit(False)) | 0 rows |
| Neutral start for AND-chains | pred = pl.lit(True) then pred &= cond | AND identity |
| Neutral start for OR-chains | pred = pl.lit(False) then `pred | = cond` |
Conclusion
The short version: there is no special always-true filter function in Polars, and you don't need one. Use pl.lit(True) for a no-op predicate that keeps every row, use pl.lit(False) to drop them all, and lean on pl.lit(True) as the identity element when you build filter predicates dynamically. A bare Python True works too on current versions, but the explicit literal reads better and sidesteps old operator-combination footguns.
If you're on an older release and a no-op filter behaves strangely with empty frames or all-null columns, upgrade — those were bugs, now fixed. Next step: check your installed version with pl.__version__, and skim the official DataFrame.filter docs for the full predicate and constraint syntax.
References
- polars.DataFrame.filter — official documentation
- polars.LazyFrame.filter — official documentation
- Issue #16664 — rowless DataFrame + literal True adds a row (fixed)
- Issue #19771 — no-op filter errors on an all-null column (fixed)
- Issue #7205 — Series.filter with a boolean literal
- Polars releases and blog
- Polars on PyPI (check the current version)

- Coding Agent Help - Wiki
- GStreamer + TypeScript: GObject Breaking Changes
- Firebase Auth Token Expiry: How Long ID Tokens Last
- i18next Supported Locale Codes: There Is No Fixed List
- BoringSSL's Latest Stable Release Version, Explained
- c8 Coverage JSON Output Flag: A Practical Guide
- OpenSSL Latest Stable Release: The 2026 Version Guide
- Polars DataFrame Filter: Always-True Predicate
- Latest Stable Version of HashiCorp Terraform (2026)
- GStreamer 1.0: The Major Version That Changed AudioFormat
- GitHub GraphQL API isTotal Field: It Doesn't Exist
