pola-rs/polars · error · TypeError

`describe_categorical` only works on categorical columns

Error message

`describe_categorical` only works on categorical columns

What it means

Raised by PolarsColumn.describe_categorical in the interchange protocol when the column's dtype is neither Categorical nor Enum. The method exists specifically to expose category metadata, so numeric/String/temporal columns are rejected with a TypeError.

Source

Thrown at py-polars/src/polars/interchange/column.py:78

    def describe_categorical(self) -> CategoricalDescription:
        """
        Description of the categorical data type of the column.

        Raises
        ------
        TypeError
            If the data type of the column is not categorical.
        """
        dtype = self._col.dtype
        if isinstance(dtype, Categorical):
            categories = self._col.unique().drop_nulls().cast(String)
            is_ordered = False
        elif isinstance(dtype, Enum):
            categories = dtype.categories
            is_ordered = True
        else:
            msg = "`describe_categorical` only works on categorical columns"
            raise TypeError(msg)

        return {
            "is_ordered": is_ordered,
            "is_dictionary": True,
            "categories": PolarsColumn(categories, allow_copy=self._allow_copy),
        }

    @property
    def describe_null(self) -> tuple[ColumnNullType, int | None]:
        """Description of the null representation the column uses."""
        if self.null_count == 0:
            return ColumnNullType.NON_NULLABLE, None
        else:
            return ColumnNullType.USE_BITMASK, 0

    @property
    def null_count(self) -> int:
        """The number of null elements."""

View on GitHub (pinned to df599052da)

Solutions

  1. Guard the call: only invoke describe_categorical when the dtype is Categorical or Enum
  2. Cast beforehand if dictionary semantics are wanted: df.with_columns(pl.col('s').cast(pl.Categorical))
  3. In consumers, rely on the interchange dtype kind (CATEGORICAL) rather than assuming string columns are categorical

Example fix

// before
info = interchange_col.describe_categorical()  # on a String column
// after
from polars.datatypes import Categorical, Enum
if isinstance(interchange_col._col.dtype, (Categorical, Enum)):
    info = interchange_col.describe_categorical()
Defensive patterns

Strategy: type-guard

Validate before calling

from polars.datatypes import Categorical, Enum

if isinstance(column._col.dtype, (Categorical, Enum)):
    info = column.describe_categorical()

Type guard

def is_categorical_column(col) -> bool:
    from polars.datatypes import Categorical, Enum
    return isinstance(col._col.dtype, (Categorical, Enum))

Try / catch

try:
    info = col.describe_categorical()
except TypeError:
    info = None  # not dictionary-encoded; treat as plain values

Prevention

When it happens

Trigger: Calling describe_categorical() on an interchange column backed by String, Int64, or any non-categorical dtype; interchange consumers that unconditionally query categorical metadata for dictionary-encoded columns marked via the data kind flags; iterating columns and calling describe_categorical based on is_dictionary heuristics.

Common situations: Writing a generic interchange consumer that assumes any object/string column is dictionary-encoded; converting polars data for libraries expecting dictionary encoding; schema drift where a column that used to be Categorical arrives as String.

Related errors


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