pola-rs/polars · error · NotImplementedError

non-string categories are not supported

Error message

non-string categories are not supported

What it means

For dictionary-encoded categorical columns imported via the interchange protocol, Polars maps categories to an Enum, which requires the categories column itself to be strings (DtypeKind.STRING). If the categories have any other kind (e.g. integer-coded categories), NotImplementedError is raised; only an empty categories column is accepted (mapped to Enum([])).

Solutions

  1. On the producer, cast the dictionary values to string (pyarrow: pc.cast(col, pa.dictionary(pa.int32(), pa.string()))).
  2. Import the column as plain integers and re-create the categorical/Enum in Polars yourself via .cast(pl.Enum(...)).
  3. Convert via pyarrow/pandas first and then pl.from_arrow/pl.from_pandas.

Example fix

# before: pyarrow dictionary with int32 values -> NotImplementedError
tbl = tbl  # column 'code' is dictionary<int32, int32>

# after
import pyarrow as pa, pyarrow.compute as pc
i = tbl.schema.get_field_index("code")
tbl = tbl.set_column(i, "code", pc.cast(tbl.column(i), pa.dictionary(pa.int32(), pa.string())))
df = pl.from_dataframe(tbl.__dataframe__())
Defensive patterns

Strategy: validation

Validate before calling

def categories_are_strings(col) -> bool:
    if col.dtype[0] != DtypeKind.CATEGORICAL:
        return True
    cat = col.describe_categorical()
    if not cat.get("is_dictionary"):
        return False
    categories = cat["categories"]
    return categories.size() == 0 or categories.dtype[0] == DtypeKind.STRING

ok = all(categories_are_strings(icdf.get_column(i)) for i in range(icdf.num_columns()))

Try / catch

try:
    df = pl.from_dataframe(obj)
except NotImplementedError as e:
    if "non-string categories" not in str(e):
        raise
    # cast the dictionary values to string on the producer (e.g. pyarrow pc.cast), then retry

Prevention

When it happens

Trigger: pl.from_dataframe(obj) where a dictionary column's categories are integers or other non-strings — e.g. a pyarrow dictionary array whose values type is int32/float, or a producer encoding labels as numeric codes.

Common situations: Storage formats that dictionary-encode numeric values; producers that reuse a generic dictionary encoder regardless of value type; pandas/duckdb pipelines that materialize integer-coded category tables.

Related errors


AI-assisted analysis of pola-rs/polars@9b5d73fd00 (2026-08-19). Data as JSON: /api/errors/e059529f9f26b9fc. Report an issue: GitHub.

Appendix: source

Thrown at py-polars/src/polars/interchange/from_dataframe.py:157

        data = pl.Series._from_buffers(
            String, data=data_buffers, validity=validity_buffer
        )

    return data


def _categorical_column_to_series(column: Column, *, allow_copy: bool) -> Series:
    categorical = column.describe_categorical
    if not categorical["is_dictionary"]:
        msg = "non-dictionary categoricals are not yet supported"
        raise NotImplementedError(msg)

    categories_col = categorical["categories"]
    if categories_col.size() == 0:
        dtype = Enum([])
    elif categories_col.dtype[0] != DtypeKind.STRING:
        msg = "non-string categories are not supported"
        raise NotImplementedError(msg)
    else:
        categories = _string_column_to_series(categories_col, allow_copy=allow_copy)
        dtype = Enum(categories)

    buffers = column.get_buffers()
    offset = column.offset

    data_buffer = _construct_data_buffer(
        *buffers["data"], column.size(), offset, allow_copy=allow_copy
    )
    validity_buffer = _construct_validity_buffer(
        buffers["validity"], column, dtype, data_buffer, offset, allow_copy=allow_copy
    )

    # First construct a physical Series without categories
    # to allow for sentinel values that do not fit in UInt32
    data_dtype = data_buffer.dtype
    out = pl.Series._from_buffers(

View on GitHub (pinned to 9b5d73fd00)