pola-rs/polars · error · ValueError

`format` must be one of {'binary', 'json'}, got {format!r}

Error message

`format` must be one of {'binary', 'json'}, got {format!r}

What it means

Expr.deserialize supports exactly two formats: 'binary' (polars' compact self-describing format) and 'json'. Any other format string — 'pickle', 'arrow', or None where a string is expected — raises ValueError after the source is normalised but before a deserializer is chosen. Serialize and deserialize must use matching formats.

Source

Thrown at py-polars/src/polars/expr/expr.py:589

        >>> expr = pl.col("foo").sum().over("bar")
        >>> bytes = expr.meta.serialize()
        >>> pl.Expr.deserialize(io.BytesIO(bytes))
        <Expr ['col("foo").sum().over([col("ba…'] at ...>
        """
        if isinstance(source, StringIO):
            source = BytesIO(source.getvalue().encode())
        elif isinstance(source, (str, Path)):
            source = normalize_filepath(source)
        elif isinstance(source, bytes):
            source = BytesIO(source)

        if format == "binary":
            deserializer = PyExpr.deserialize_binary
        elif format == "json":
            deserializer = PyExpr.deserialize_json
        else:
            msg = f"`format` must be one of {{'binary', 'json'}}, got {format!r}"
            raise ValueError(msg)

        return cls._from_pyexpr(deserializer(source))

    def to_physical(self) -> Expr:
        """
        Cast to physical representation of the logical dtype.

        - :func:`polars.datatypes.Date` -> :func:`polars.datatypes.Int32`
        - :func:`polars.datatypes.Datetime` -> :func:`polars.datatypes.Int64`
        - :func:`polars.datatypes.Time` -> :func:`polars.datatypes.Int64`
        - :func:`polars.datatypes.Duration` -> :func:`polars.datatypes.Int64`
        - :func:`polars.datatypes.Categorical` -> :func:`polars.datatypes.UInt32`
        - `List(inner)` -> `List(physical of inner)`
        - `Array(inner)` -> `Struct(physical of inner)`
        - `Struct(fields)` -> `Array(physical of fields)`

        Other data types will be left unchanged.

View on GitHub (pinned to df599052da)

Solutions

  1. Use 'binary' (compact default) or 'json' (debuggable) exactly
  2. Pair formats on both sides: expr.serialize(format='json') with pl.Expr.deserialize(src, format='json')
  3. If pickle is needed, pickle the produced bytes instead: pickle.dumps(expr.serialize())

Example fix

# before
expr = pl.Expr.deserialize(data, format='pickle')  # ValueError

# after
expr = pl.Expr.deserialize(data, format='binary')
# round-trip: blob = expr.serialize(format='json'); expr = pl.Expr.deserialize(blob, format='json')
Defensive patterns

Strategy: validation

Validate before calling

VALID_FORMATS = ('binary', 'json')

if fmt not in VALID_FORMATS:
    raise ValueError(f"format must be one of {VALID_FORMATS}")
expr = pl.Expr.deserialize(source, format=fmt)

Type guard

from typing import Literal, TypeGuard

DeserializeFormat = Literal['binary', 'json']

def is_deserialize_format(v: str) -> TypeGuard[DeserializeFormat]:
    return v in ('binary', 'json')

Prevention

When it happens

Trigger: pl.Expr.deserialize(source, format='pickle'); format=None on a code path that forwards it verbatim; typos like 'jsn' or 'JSON'.

Common situations: Caching serialised expressions inside pickle-based pipelines; interop code assuming pickle because other libraries default to it; format strings threaded through from config without validation.

Related errors


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