pola-rs/polars · error · ValueError
data type {dtype!r} not supported by the interchange protoco
Error message
data type {dtype!r} not supported by the interchange protocol What it means
On the export side (Polars DataFrame exposed through the interchange protocol), polars_dtype_to_dtype maps Polars dtypes to protocol dtype tuples via a lookup map. Types with no protocol representation - nested types (List, Struct, Array), Binary, Time, Null, Object - hit a KeyError that is re-raised as ValueError. Only primitive numeric, boolean, string, datetime, date, duration, categorical and enum types can be exported.
Source
Thrown at py-polars/src/polars/interchange/utils.py:65
Float64: (DtypeKind.FLOAT, 64, "g", NE),
Boolean: (DtypeKind.BOOL, 1, "b", NE),
String: (DtypeKind.STRING, 8, "U", NE),
Date: (DtypeKind.DATETIME, 32, "tdD", NE),
Time: (DtypeKind.DATETIME, 64, "ttu", NE),
Datetime: (DtypeKind.DATETIME, 64, "tsu:", NE),
Duration: (DtypeKind.DATETIME, 64, "tDu", NE),
Categorical: (DtypeKind.CATEGORICAL, 32, "I", NE),
Enum: (DtypeKind.CATEGORICAL, 32, "I", NE),
}
def polars_dtype_to_dtype(dtype: PolarsDataType) -> Dtype:
"""Convert Polars data type to interchange protocol data type."""
try:
result = polars_dtype_to_dtype_map[dtype.base_type()]
except KeyError as exc:
msg = f"data type {dtype!r} not supported by the interchange protocol"
raise ValueError(msg) from exc
# Handle instantiated data types
if isinstance(dtype, Datetime):
return _datetime_to_dtype(dtype)
elif isinstance(dtype, Duration):
return _duration_to_dtype(dtype)
return result
def _datetime_to_dtype(dtype: Datetime) -> Dtype:
tu = dtype.time_unit[0]
tz = dtype.time_zone if dtype.time_zone is not None else ""
arrow_c_type = f"ts{tu}:{tz}"
return DtypeKind.DATETIME, 64, arrow_c_type, NE
def _duration_to_dtype(dtype: Duration) -> Dtype:View on GitHub (pinned to df599052da)
Solutions
- Select or drop unsupported columns before conversion: df.select([c for c, d in zip(df.columns, df.dtypes) if is_exportable(d)])
- Cast nested columns to a representable form first (e.g. List -> String via str serialization, or explode them)
- Transfer the data with Arrow instead (df.to_arrow()), which supports nested types, instead of the deprecated interchange path
Example fix
// before df.__dataframe__() # contains List column -> ValueError // after exportable = [name for name, dtype in zip(df.columns, df.dtypes) if dtype.is_integer() or dtype.is_float() or dtype == pl.String or dtype.is_temporal() or dtype == pl.Boolean or dtype in (pl.Categorical, pl.Enum)] df.select(exportable).__dataframe__()
Defensive patterns
Strategy: validation
Validate before calling
import polars as pl
from polars.interchange.utils import polars_dtype_to_dtype
def exportable_columns(df: pl.DataFrame) -> list[str]:
names = []
for name, dtype in zip(df.columns, df.dtypes):
try:
polars_dtype_to_dtype(dtype)
except ValueError:
continue
names.append(name)
return names
# usage: df.select(exportable_columns(df)).__dataframe__() Type guard
import polars as pl
from polars.interchange.utils import polars_dtype_to_dtype
def dtype_is_interchange_exportable(dtype: pl.DataType) -> bool:
"""True if polars_dtype_to_dtype(dtype) succeeds."""
try:
polars_dtype_to_dtype(dtype)
except ValueError:
return False
return True Try / catch
try:
proto = df.__dataframe__()
except ValueError as e:
if 'not supported by the interchange protocol' in str(e):
raise ValueError(
f'frame has non-exportable dtypes: {df.schema}'
) from e
raise Prevention
- Filter frames to primitive dtypes (numeric, boolean, string, temporal, categorical/enum) before interchange export
- Watch for List/Struct columns introduced by aggregations - explode or cast them first
- Prefer Arrow (df.to_arrow()) when nested, Binary, Null, Object, or Time columns must be transferred
When it happens
Trigger: Calling df.__dataframe__() on a Polars DataFrame, or passing one to another library's interchange consumer, when the frame contains List/Struct/Array/Binary/Null/Object/Time columns.
Common situations: Feeding polars output into interchange-based consumers (older ibis, vaex, plotting tools); schema drift after group_by/agg/window operations that silently produce List columns; concat growing Null-typed columns.
Related errors
- cannot get buffer length for buffer with dtype {dtype!r}
- unsupported data type: {dtype}
- unsupported data type: {dtype!r}
- cannot treat NumPy array of type {arr.dtype} as indices
- reinterpret requires exactly one of `signed` or `dtype` to b
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/31416330151aee5a.
Report an issue: GitHub.