pandas-dev/pandas · error · ValueError

invalid dtype: {dtype}

Error message

invalid dtype: {dtype}

What it means

Raised by the internal `IntervalArray._validate` when the supplied dtype is not an `IntervalDtype`. This typically means a downstream caller passed a plain numpy/object dtype into a code path that requires a properly-formed IntervalDtype. Fires at pandas/core/arrays/interval.py:609.

Source

Thrown at pandas/core/arrays/interval.py:609

            right.append(rhs)

        return cls.from_arrays(left, right, closed, copy=False, dtype=dtype)

    @classmethod
    def _validate(cls, left, right, dtype: IntervalDtype) -> None:
        """
        Verify that the IntervalArray is valid.

        Checks that

        * dtype is correct
        * left and right match lengths
        * left and right have the same missing values
        * left is always below right
        """
        if not isinstance(dtype, IntervalDtype):
            msg = f"invalid dtype: {dtype}"
            raise ValueError(msg)
        if len(left) != len(right):
            msg = "left and right must have the same length"
            raise ValueError(msg)
        left_mask = notna(left)
        right_mask = notna(right)
        if not (left_mask == right_mask).all():
            msg = (
                "missing values must be missing in the same "
                "location both left and right sides"
            )
            raise ValueError(msg)
        if not (left[left_mask] <= right[left_mask]).all():
            msg = "left side of interval must be <= right side"
            raise ValueError(msg)

    def _shallow_copy(self, left, right) -> Self:
        """
        Return a new IntervalArray with the replacement attributes

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Wrap the subtype: `dtype=pd.IntervalDtype('int64')` or `dtype='interval[int64]'`.
  2. Drop `dtype=` entirely and let pandas infer the subtype from the bounds.
  3. If subclassing, ensure `_validate` receives `IntervalDtype`, not the raw subtype.

Example fix

// before
pd.IntervalIndex(left, right, dtype='int64')
// after
pd.IntervalIndex(left, right, dtype='interval[int64]')
Defensive patterns

Strategy: validation

Validate before calling

import pandas as pd

def as_interval_dtype(dtype):
    if dtype is None:
        return None
    if not isinstance(dtype, pd.IntervalDtype):
        dtype = pd.IntervalDtype(dtype)
    return dtype

Type guard

import pandas as pd

def is_interval_dtype(dtype) -> bool:
    return isinstance(dtype, pd.IntervalDtype)

Try / catch

try:
    ii = pd.IntervalIndex(left, right, dtype=dtype)
except ValueError as e:
    if "invalid dtype" in str(e):
        ii = pd.IntervalIndex(left, right, dtype=pd.IntervalDtype(dtype))
    else:
        raise

Prevention

When it happens

Trigger: Internal calls into `_validate(left, right, dtype='int64')`, or a subclass/factory that hands a non-Interval dtype to the validator. Public API users usually hit it via `pd.IntervalIndex(..., dtype='int64')` instead of `dtype='interval[int64]'`.

Common situations: Passing the subtype dtype directly rather than wrapping it in IntervalDtype; copy-paste errors from numeric code paths.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/3c9d04a34bdcac7b. Report an issue: GitHub.