pandas-dev/pandas · error · ValueError

Cannot convert float NaN to integer

Error message

Cannot convert float NaN to integer

What it means

Raised by Categorical.astype when casting to an integer dtype ('i' or 'u' kind) while the categorical contains missing values (NaN, coded internally as -1). Integers cannot represent NaN, so pandas refuses rather than silently coercing to a sentinel.

Source

Thrown at pandas/core/arrays/categorical.py:623

            # GH 10696/18593/18630
            dtype = self.dtype.update_dtype(dtype)
            self = self.copy() if copy else self
            result = self._set_dtype(dtype, copy=False)
            wrong = result.isna() & ~self.isna()
            if wrong.any():
                warnings.warn(
                    "Constructing a Categorical with a dtype and values containing "
                    "non-null entries not in that dtype's categories is deprecated "
                    "and will raise in a future version.",
                    Pandas4Warning,
                    stacklevel=find_stack_level(),
                )

        elif isinstance(dtype, ExtensionDtype):
            return super().astype(dtype, copy=copy)

        elif dtype.kind in "iu" and self.isna().any():
            raise ValueError("Cannot convert float NaN to integer")

        elif len(self.codes) == 0 or len(self.categories) == 0:
            # For NumPy 1.x compatibility we cannot use copy=None.  And
            # `copy=False` has the meaning of `copy=None` here:
            if not copy:
                result = np.asarray(self, dtype=dtype)
            else:
                result = np.array(self, dtype=dtype)

        else:
            # GH8628 (PERF): astype category codes instead of astyping array
            new_cats = self.categories._values

            try:
                new_cats = new_cats.astype(dtype=dtype, copy=copy)
                fill_value = self.categories._na_value
                if not is_valid_na_for_dtype(fill_value, dtype):
                    fill_value = lib.item_from_zerodim(

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Fill missing values first: `cat.fillna(...).astype('int')` or use a sentinel category.
  2. Cast to pandas' nullable integer: `cat.astype('Int64')` (Int64 accepts pd.NA).
  3. Drop NaN rows: `cat.dropna().astype('int')` if appropriate.
  4. Operate on codes directly via `cat.codes` (already int, with -1 for missing).

Example fix

# before
cat = pd.Categorical(['a', None, 'b'])
cat.astype(int)
# after
cat = pd.Categorical(['a', None, 'b'])
cat.astype('Int64')  # nullable integer, then map if needed
Defensive patterns

Strategy: validation

Validate before calling

def cat_to_int(cat):
    if cat.isna().any():
        return cat.astype('Int64')  # nullable int
    return cat.astype('int64')

Type guard

def has_no_na(cat) -> bool:
    return not bool(cat.isna().any())

Try / catch

try:
    out = cat.astype('int')
except ValueError as e:
    if 'NaN to integer' in str(e):
        out = cat.astype('Int64')
    else:
        raise

Prevention

When it happens

Trigger: `cat.astype('int')`, `cat.astype(np.int64)`, or `cat.astype('Int64')`-via-int path when `cat.isna().any()` is True. Commonly hit when a categorical came from data with missing entries.

Common situations: Reading survey/enum data with blanks, then converting codes to int for modeling; or downstream code assuming no missing values.

Related errors


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