pola-rs/polars · error · TypeError
cannot describe a LazyFrame that has no columns
Error message
cannot describe a LazyFrame that has no columns
What it means
LazyFrame.describe() computes summary statistics per column, which is impossible when the frame's collected schema is empty (zero columns). Polars raises a TypeError rather than returning an empty summary.
Solutions
- Guard with `if lf.collect_schema(): lf.describe()` before calling
- Ensure the pipeline retains at least one column before describing
- Row-empty frames with columns are fine — only zero-column schemas fail
Example fix
// before summary = lf.describe() // after summary = lf.describe() if lf.collect_schema() else None
Defensive patterns
Strategy: validation
Validate before calling
schema = lf.collect_schema()
if not schema:
raise ValueError("LazyFrame has no columns; cannot describe") Type guard
def describable(lf: pl.LazyFrame) -> bool:
return len(lf.collect_schema()) > 0 Try / catch
try:
summary = lf.describe()
except TypeError as e:
if "no columns" in str(e):
summary = None
else:
raise Prevention
- Check collect_schema() non-empty before describe()
- Avoid select() chains that can drop all columns
- Handle empty schemas at pipeline boundaries
When it happens
Trigger: `lf.describe()` on an empty-schema frame, e.g. `pl.LazyFrame()`, or after a select() that drops all columns.
Common situations: Describing dynamically-built frames where upstream selects removed every column; conditional column selection yielding zero columns; empty templates not yet populated.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- LazyFrame is not subscriptable (aside from slicing) Use…
- `limit` cannot be None. If you want to show the complete…
- negative slice lengths
- " " comparison not supported for LazyFrame objects
- `pivot` needs either `index or `values` needs to be…
AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18).
Data as JSON: /api/errors/de28a7cb9b747063.
Report an issue: GitHub.
Appendix: source
Thrown at py-polars/src/polars/lazyframe/frame.py:1161
│ null_count ┆ 0.0 ┆ 1.0 ┆ 0.0 ┆ 0 ┆ 0 ┆ 0 │
│ mean ┆ 2.266667 ┆ 45.0 ┆ 0.666667 ┆ null ┆ 2021-07-02 16:00:00 ┆ 16:07:10 │
│ std ┆ 1.101514 ┆ 7.071068 ┆ null ┆ null ┆ null ┆ null │
│ min ┆ 1.0 ┆ 40.0 ┆ 0.0 ┆ xx ┆ 2020-01-01 ┆ 10:20:30 │
│ 10% ┆ 1.36 ┆ 41.0 ┆ null ┆ null ┆ 2020-04-20 ┆ 11:13:34 │
│ 30% ┆ 2.08 ┆ 43.0 ┆ null ┆ null ┆ 2020-11-26 ┆ 12:59:42 │
│ 50% ┆ 2.8 ┆ 45.0 ┆ null ┆ null ┆ 2021-07-05 ┆ 14:45:50 │
│ 70% ┆ 2.88 ┆ 47.0 ┆ null ┆ null ┆ 2022-02-07 ┆ 18:09:34 │
│ 90% ┆ 2.96 ┆ 49.0 ┆ null ┆ null ┆ 2022-09-13 ┆ 21:33:18 │
│ max ┆ 3.0 ┆ 50.0 ┆ 1.0 ┆ zz ┆ 2022-12-31 ┆ 23:15:10 │
└────────────┴──────────┴──────────┴──────────┴──────┴─────────────────────┴──────────┘
""" # noqa: W505
from polars.convert import from_dict
schema = self.collect_schema()
if not schema:
msg = "cannot describe a LazyFrame that has no columns"
raise TypeError(msg)
# create list of metrics
metrics = ["count", "null_count", "mean", "std", "min"]
if quantiles := parse_percentiles(percentiles):
metrics.extend(f"{q * 100:g}%" for q in quantiles)
metrics.append("max")
@lru_cache
def skip_minmax(dt: PolarsDataType) -> bool:
return (
dt.is_nested()
or dt.is_extension()
or dt in (Categorical, Enum, Null, Object, Unknown)
)
# determine which columns will produce std/mean/percentile/etc
# statistics in a single pass over the frame schema
has_numeric_result, sort_cols = set(), set()View on GitHub (pinned to fe841f959e)