pola-rs/polars · error · ValueError

data type not supported by the interchange protocol

Error message

data type {dtype!r} not supported by the interchange protocol

What it means

When a Polars DataFrame is exported through the dataframe interchange protocol, each column's dtype is mapped via a fixed lookup table covering primitives (bool, ints, floats), String, Date, Datetime, Duration, Categorical and Enum. Any dtype outside that table — nested types (List, Struct, Array), Binary, Object, Null, Decimal, Time — raises ValueError('data type ... not supported by the interchange protocol').

Solutions

  1. Drop or stringify unsupported columns before interchange: df.select(pl.col('nested').cast(pl.String)) or .drop('nested').
  2. Use the Arrow path for nested data: df.write_arrow / pl.from_arrow, which supports List and Struct natively.
  3. If the consumer supports it, serialize nested columns to JSON strings and decode after transfer.

Example fix

# before
icdf = df.__dataframe__()  # List/Struct/Object column -> ValueError

# after
df_flat = df.with_columns(pl.col("payload").cast(pl.String))  # or .drop("payload")
icdf = df_flat.__dataframe__()
Defensive patterns

Strategy: validation

Validate before calling

INTERCHANGE_SUPPORTED = {
    pl.Boolean, pl.Int8, pl.Int16, pl.Int32, pl.Int64,
    pl.UInt8, pl.UInt16, pl.UInt32, pl.UInt64,
    pl.Float32, pl.Float64, pl.String, pl.Date,
    pl.Datetime, pl.Duration, pl.Categorical, pl.Enum,
}

unsupported = [
    name for name, dtype in df.schema.items()
    if dtype.base_type() not in INTERCHANGE_SUPPORTED
]
export_df = df.drop(unsupported) if unsupported else df
icdf = export_df.__dataframe__()

Type guard

def interchange_safe(df: pl.DataFrame) -> bool:
    nested_or_exotic = {pl.List, pl.Struct, pl.Array, pl.Object, pl.Binary, pl.Null}
    return all(dt.base_type() not in nested_or_exotic for dt in df.schema.values())

Prevention

When it happens

Trigger: An interchange consumer (another library calling df.__dataframe__()) inspects a Polars DataFrame containing List/Struct/Array/Object/Binary columns; also selecting such columns before exporting via the protocol. The Datetime/Duration entries are special-cased afterward for time unit and time zone.

Common situations: Feeding nested Polars data to interchange-only consumers; ML/interop tools that only speak the interchange protocol; mixed frames where one Object column (from .to_objects or Python objects) breaks the whole export.

Related errors


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

Appendix: 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 9b5d73fd00)