pandas-dev/pandas · error · ValueError

dtype must be PeriodDtype

Error message

dtype must be PeriodDtype

What it means

Raised by pandas.core.arrays.period.validate_dtype_freq when an explicitly passed dtype is non-None but is not a PeriodDtype instance. Period arrays require their dtype to carry a frequency, so a plain dtype like 'int64' or 'datetime64[ns]' is rejected at construction. The check guards the dtype branch (dtype is not None) before any freq reconciliation happens.

Solutions

  1. Drop the dtype argument entirely and let pandas infer PeriodDtype from freq or from the Period objects.
  2. Pass dtype as a PeriodDtype constructed via pd.PeriodDtype(freq) e.g. pd.PeriodDtype('D').
  3. If you only have a freq string, pass freq='D' instead of dtype='datetime64[ns]'.

Example fix

# before
pd.PeriodIndex(['2020-01-01'], dtype='datetime64[ns]', freq='D')
# after
pd.PeriodIndex(['2020-01-01'], freq='D')
# or
pd.PeriodIndex(['2020-01-01'], dtype=pd.PeriodDtype('D'))
Defensive patterns

Strategy: validation

Validate before calling

from pandas.api.types import pandas_dtype as _pdtype
from pandas import PeriodDtype

def ensure_period_dtype(dtype):
    if dtype is None or isinstance(dtype, PeriodDtype):
        return dtype
    raise ValueError(f'dtype must be PeriodDtype, got {dtype!r}')

Type guard

from pandas import PeriodDtype

def is_period_dtype(dtype) -> bool:
    return isinstance(dtype, PeriodDtype)

Try / catch

from pandas.errors import IncompatibleFrequency
try:
    idx = pd.PeriodIndex(data, freq=freq, dtype=cand_dtype)
except (ValueError, IncompatibleFrequency) as e:
    if 'must be PeriodDtype' in str(e):
        idx = pd.PeriodIndex(data, freq=freq)
    else:
        raise

Prevention

When it happens

Trigger: Constructing PeriodArray/PeriodIndex/period_range with dtype= something that is not PeriodDtype (e.g. dtype='int64', dtype=np.int64, dtype='datetime64[ns]', dtype='object'). Passing a pandas_dtype that resolves to a non-Period extension or numpy dtype while freq is also given.

Common situations: Mistakenly thinking PeriodIndex is built from datetime64 dtype; copy-pasting dtype from a DatetimeIndex workflow; constructing PeriodArray from a list of Period objects and supplying a stray dtype keyword from a generic helper.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/3d2f73b3cfa92f26. Report an issue: GitHub.

Appendix: source

Thrown at pandas/core/arrays/period.py:1453

    Parameters
    ----------
    dtype : dtype
    dtype2 : PeriodDtype or None
        Dtype derived from a `freq` passed to the caller.

    Returns
    -------
    PeriodDtype or None

    Raises
    ------
    ValueError : non-period dtype
    IncompatibleFrequency : mismatch between dtype and freq
    """
    if dtype is not None:
        if not isinstance(dtype, PeriodDtype):
            raise ValueError("dtype must be PeriodDtype")
        if dtype2 is not None and dtype != dtype2:
            raise IncompatibleFrequency("specified freq and dtype are different")

    elif dtype2 is not None:
        if not isinstance(dtype2, PeriodDtype):
            raise ValueError("dtype must be PeriodDtype")
        dtype = dtype2

    return dtype


def dt64arr_to_periodarr(
    data, freq, tz=None
) -> tuple[npt.NDArray[np.int64], BaseOffset]:
    """
    Convert a datetime-like array to values Period ordinals.

    Parameters

View on GitHub (pinned to 3b7651241d)