pandas-dev/pandas · error · TypeError

cannot safely cast non-equivalent

Error message

cannot safely cast non-equivalent {values.dtype} to {np.dtype(dtype)}

What it means

Raised by `IntegerArray._safe_cast` (NumericArray path) when lossless casting is required but impossible. It first tries numpy `astype(..., casting='safe')`; on failure it does an unsafe cast and compares element-wise — if any value changed, the cast is not equivalent and the operation is rejected. This protects integer ExtensionArrays from silently truncating floats or narrowing integers.

Solutions

  1. Round or floor the floats first if truncation is intended: `s.round().astype('Int64')` or `np.floor(s).astype('Int64')`.
  2. Use a nullable float dtype (`'Float64'`) instead of integer if fractional values are valid.
  3. Drop or correct the non-integer rows before casting: `s[s == s.floor()].astype('Int64')`.
  4. Pass `copy=False, casting='unsafe'` only via the underlying numpy array if you accept the loss; do not rely on the ExtensionArray path to do this.

Example fix

# before
pd.array([1.5, 2.0, 3.0], dtype='Int64')  # 1.5 is non-equivalent

# after - decide semantics explicitly
pd.array([1.5, 2.0, 3.0], dtype='Float64')        # keep precision
pd.array(np.floor([1.5, 2.0, 3.0]).astype(int), dtype='Int64')  # truncate
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np
def safe_to_int(s, dtype='Int64'):
    s = pd.Series(s)
    if np.issubdtype(s.dtype, np.floating):
        if not (s.dropna() == s.dropna().floor()).all():
            raise ValueError('non-integer floats present; cannot safely cast to Int')
    return s.astype(dtype)

Type guard

def is_lossless_int_castable(values: np.ndarray, dtype) -> bool:
    try:
        casted = values.astype(dtype, casting='safe')
        return True
    except TypeError:
        casted = values.astype(dtype)
        return (casted == values).all()

Try / catch

try:
    arr = pd.array(values, dtype='Int64')
except TypeError as e:
    if 'cannot safely cast non-equivalent' in str(e):
        arr = pd.array(values, dtype='Float64')  # or floor first
    else:
        raise

Prevention

When it happens

Trigger: Assigning a float array containing non-integer values (e.g. 1.5) into an IntegerArray/Int64 column; constructing `pd.array([1.5, 2.0], dtype='Int64')`; `astype('Int64')` on a float Series with fractional values; read_csv inferring float then coercing to nullable Int with non-integer data.

Common situations: Loading data with NaN-imputed floats into an 'Int64' column where imputation produced fractional values; merging/joining that upcasts to float64 then casting back to Int32; user passes `dtype='Int8'` to a column whose values exceed the range; arithmetic producing floats assigned back to integer nullable dtype.

Related errors


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

Appendix: source

Thrown at pandas/core/arrays/integer.py:69

    def _get_dtype_mapping(cls) -> dict[np.dtype, IntegerDtype]:
        return NUMPY_INT_TO_DTYPE

    @classmethod
    def _safe_cast(cls, values: np.ndarray, dtype: np.dtype, copy: bool) -> np.ndarray:
        """
        Safely cast the values to the given dtype.

        "safe" in this context means the casting is lossless. e.g. if 'values'
        has a floating dtype, each value must be an integer.
        """
        try:
            return values.astype(dtype, casting="safe", copy=copy)
        except TypeError as err:
            casted = values.astype(dtype, copy=copy)
            if (casted == values).all():
                return casted

            raise TypeError(
                f"cannot safely cast non-equivalent {values.dtype} to {np.dtype(dtype)}"
            ) from err


@set_module("pandas.arrays")
class IntegerArray(NumericArray):
    """
    Array of integer (optional missing) values.

    Uses :attr:`pandas.NA` as the missing value.

    .. warning::

       IntegerArray is currently experimental, and its API or internal
       implementation may change without warning.

    We represent an IntegerArray with 2 numpy arrays:

View on GitHub (pinned to 3b7651241d)