pandas-dev/pandas · error · TypeError

dtype must be an IntervalDtype, got {dtype}

Error message

dtype must be an IntervalDtype, got {dtype}

What it means

Raised by IntervalArray._ensure_simple_new_inputs (via _simple_new validation) when a dtype is supplied but, after pandas_dtype resolution, it is not an IntervalDtype. Interval arrays require an IntervalDtype subtype; any other dtype (int64, float64, category, etc.) is rejected. TypeError echoing the bad dtype.

Source

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

        left = ensure_index(left, copy=copy)

        right = ensure_index(right, copy=copy)

        if closed is None and isinstance(dtype, IntervalDtype):
            closed = dtype.closed

        closed = closed or "right"

        if dtype is not None:
            # GH 19262: dtype must be an IntervalDtype to override inferred
            dtype = pandas_dtype(dtype)
            if isinstance(dtype, IntervalDtype):
                if dtype.subtype is not None:
                    left = left.astype(dtype.subtype)
                    right = right.astype(dtype.subtype)
            else:
                msg = f"dtype must be an IntervalDtype, got {dtype}"
                raise TypeError(msg)

            if dtype.closed is None:
                # possibly loading an old pickle
                dtype = IntervalDtype(dtype.subtype, closed)
            elif closed != dtype.closed:
                raise ValueError("closed keyword does not match dtype.closed")

        # coerce dtypes to match if needed
        if is_float_dtype(left.dtype) and is_integer_dtype(right.dtype):
            right = right.astype(left.dtype)
        elif is_float_dtype(right.dtype) and is_integer_dtype(left.dtype):
            left = left.astype(right.dtype)

        if type(left) != type(right):
            msg = (
                f"must not have differing left [{type(left).__name__}] and "
                f"right [{type(right).__name__}] types"
            )

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Use an IntervalDtype, e.g. dtype='interval[int64]' or dtype=pd.IntervalDtype('int64').
  2. Omit dtype and let pandas infer it from the interval data.
  3. To change the bound precision, set the subtype inside the IntervalDtype string, not as a bare dtype.

Example fix

# before
pd.IntervalIndex.from_breaks([0, 1, 2], dtype='int64')
# after
pd.IntervalIndex.from_breaks([0, 1, 2], dtype='interval[int64]')
Defensive patterns

Strategy: validation

Validate before calling

def validate_interval_dtype(dtype):
    if dtype is not None and not isinstance(pd.api.pandas_dtype(dtype), pd.IntervalDtype):
        raise TypeError(f'dtype must be IntervalDtype, got {dtype!r}')
    return dtype

Type guard

def is_interval_dtype(dtype) -> bool:
    return isinstance(pd.api.pandas_dtype(dtype), pd.IntervalDtype) if dtype is not None else False

Prevention

When it happens

Trigger: pd.arrays.IntervalArray(data, dtype='int64'); pd.IntervalIndex(..., dtype='float64'); passing a numeric or extension dtype that is not an IntervalDtype.

Common situations: Confusing the subtype (the inner numeric dtype) with the array dtype; passing the underlying bound dtype instead of 'interval[...]'.

Related errors


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