pola-rs/polars · error

can't convert {pyseries.dtype()} to Decimal

Error message

can't convert {pyseries.dtype()} to Decimal

What it means

When a Series must become Decimal (explicit dtype=pl.Decimal or a cast), polars only has conversion paths for strings (scale inferred), floats (via string to infer scale), integers/Null (scale 0). Every other dtype — Boolean, Date/Datetime, Categorical, List — has no defined decimal meaning and raises this TypeError.

Source

Thrown at py-polars/src/polars/_utils/construction/series.py:194

            if pyseries.dtype() != dtype:
                pyseries = pyseries.cast(dtype, strict=strict, wrap_numerical=False)

        # Uninstanced Decimal is a bit special and has various inference paths
        if dtype == Decimal:
            if pyseries.dtype() == String:
                pyseries = pyseries.str_to_decimal_infer(inference_length=0)
            elif pyseries.dtype().is_float():
                # Go through string so we infer an appropriate scale.
                pyseries = pyseries.cast(
                    String, strict=strict, wrap_numerical=False
                ).str_to_decimal_infer(inference_length=0)
            elif pyseries.dtype().is_integer() or pyseries.dtype() == Null:
                pyseries = pyseries.cast(
                    Decimal(scale=0), strict=strict, wrap_numerical=False
                )
            elif not isinstance(pyseries.dtype(), Decimal):
                msg = f"can't convert {pyseries.dtype()} to Decimal"
                raise TypeError(msg)

        return pyseries

    elif dtype == Struct:
        # This is very bad. Goes via rows? And needs to do outer nullability separate.
        # It also has two data passes.
        # TODO: eventually go into struct builder
        struct_schema = dtype.to_schema() if isinstance(dtype, Struct) else None
        empty = {}  # type: ignore[var-annotated]

        data = []
        invalid = []
        for i, v in enumerate(values):
            if v is None:
                invalid.append(i)
                data.append(empty)
            else:
                data.append(v)

View on GitHub (pinned to df599052da)

Solutions

  1. Cast to Int64 first: s.cast(pl.Int64).cast(pl.Decimal(precision, scale)).
  2. For booleans map to 0/1: s.cast(pl.Int8).cast(pl.Decimal).
  3. For text-based decimal values, go through String: pl.Series(s, dtype=pl.Decimal) infers scale from strings.
  4. Skip/guard non-numeric columns instead of blanket-casting the whole frame.

Example fix

// before
s = pl.Series([True, False, True]).cast(pl.Decimal)

// after
s = pl.Series([True, False, True]).cast(pl.Int8).cast(pl.Decimal(10, 0))
Defensive patterns

Strategy: type-guard

Validate before calling

s = pl.Series(values)
if s.dtype not in (pl.String, pl.Null) and not (s.dtype.is_integer() or s.dtype.is_float()):
    s = s.cast(pl.Int64)  # or skip / raise for your domain
s = s.cast(pl.Decimal(precision, scale))

Type guard

def is_decimal_castable(dtype: pl.DataType) -> bool:
    return dtype.is_integer() or dtype.is_float() or dtype in (pl.String, pl.Null)

Try / catch

try:
    out = s.cast(pl.Decimal(38, 10))
except TypeError as e:
    if "to Decimal" in str(e):
        out = s.cast(pl.Int64).cast(pl.Decimal(38, 10))
    else:
        raise

Prevention

When it happens

Trigger: pl.Series([True, False]).cast(pl.Decimal); pl.Series([date(2020,1,1)]).cast(pl.Decimal); pl.Series("d", [True], dtype=pl.Decimal).

Common situations: Financial pipelines casting every numeric-looking column to Decimal where a column turned out boolean/temporal; schema drift after an upstream change; blanket .cast(pl.Decimal(precision, scale)) over all columns.

Related errors


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