pola-rs/polars · error · ValueError

`dtype` must be of type {Date, Datetime, Time}

Error message

`dtype` must be of type {Date, Datetime, Time}

What it means

Expr.str.strptime is a dispatcher over the target temporal dtype: dtype=pl.Date routes to to_date, pl.Datetime to to_datetime (honouring time_unit/time_zone if given an instantiated class), and pl.Time to to_time. Any dtype outside {Date, Datetime, Time} — numeric types, String, Duration, None — reaches the else branch and raises ValueError, because string parsing to that type is undefined.

Source

Thrown at py-polars/src/polars/expr/string.py:332

        if dtype == Date:
            return self.to_date(format, strict=strict, exact=exact, cache=cache)
        elif dtype == Datetime:
            time_unit = getattr(dtype, "time_unit", None)
            time_zone = getattr(dtype, "time_zone", None)
            return self.to_datetime(
                format,
                time_unit=time_unit,
                time_zone=time_zone,
                strict=strict,
                exact=exact,
                cache=cache,
                ambiguous=ambiguous,
            )
        elif dtype == Time:
            return self.to_time(format, strict=strict, cache=cache)
        else:
            msg = "`dtype` must be of type {Date, Datetime, Time}"
            raise ValueError(msg)

    @deprecate_nonkeyword_arguments(allowed_args=["self"], version="1.20.0")
    @unstable()
    def to_decimal(self, *, scale: int) -> Expr:
        """
        Convert a String column into a Decimal column.

        .. engine-support:: in-memory, streaming, distributed

        .. warning::
            This functionality is considered **unstable**. It may be changed
            at any point without it being considered a breaking change.

        .. versionchanged:: 1.20.0
            Parameter `inference_length` should now be passed as a keyword argument.

        .. versionchanged:: 1.33.0
            Parameter `inference_length` was removed and `scale` was made non-optional.

View on GitHub (pinned to df599052da)

Solutions

  1. For numeric-looking strings use .cast(pl.Int64) (add .str.strip_chars() if needed) — str.strptime is temporal-only.
  2. For timestamps pass pl.Date, pl.Datetime, or pl.Time.
  3. In generic pipelines, branch: temporal spec -> str.strptime, otherwise -> cast.

Example fix

# before
pl.col('s').str.strptime(pl.Int64, strict=False)

# after
pl.col('s').cast(pl.Int64, strict=False)
# temporal stays:
pl.col('s').str.strptime(pl.Datetime, '%Y-%m-%d')
Defensive patterns

Strategy: type-guard

Validate before calling

import polars as pl
from polars import Date, Datetime, Time
assert dtype in (Date, Datetime, Time) or isinstance(dtype, (Date, Datetime, Time)), f'strptime dtype must be temporal, got {dtype!r}'

Type guard

import polars as pl

def is_temporal_dtype(dt) -> bool:
    return dt in (pl.Date, pl.Datetime, pl.Time) or isinstance(dt, (pl.Date, pl.Datetime, pl.Time))

Prevention

When it happens

Trigger: pl.col('s').str.strptime(pl.Int64), str.strptime(None) via an unset variable, or str.strptime(pl.String). Parametrized temporal classes (pl.Datetime('ms')) are fine — they compare equal to the base class.

Common situations: Parsing strings that actually hold numbers (e.g. '123') with strptime instead of cast; dtype passed through a generic column-spec where non-temporal types slip in; forgetting that ISO strings to Duration is not supported via strptime.

Related errors


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