pandas-dev/pandas · error · ValueError

cannot convert float NaN to bool

Error message

cannot convert float NaN to bool

What it means

Raised by BaseMaskedArray._astype when casting a masked array with missing values to a bool numpy dtype. numpy's astype_nansafe would convert np.nan to True, which is wrong, so pandas refuses up front. A compatible na_value must be supplied (e.g. False) or NAs removed first.

Source

Thrown at pandas/core/arrays/masked.py:808

        na_value: float | np.datetime64 | lib.NoDefault

        # coerce
        if dtype.kind == "f":
            # In astype, we consider dtype=float to also mean na_value=np.nan
            na_value = np.nan
        elif dtype.kind == "M":
            unit = np.datetime_data(dtype)[0]
            na_value = np.datetime64("NaT", unit)  # type: ignore[call-overload]
        else:
            na_value = lib.no_default

        # to_numpy will also raise, but we get somewhat nicer exception messages here
        if dtype.kind in "iu" and self._hasna:
            raise ValueError("cannot convert NA to integer")
        if dtype.kind == "b" and self._hasna:
            # careful: astype_nansafe converts np.nan to True
            raise ValueError("cannot convert float NaN to bool")

        data = self.to_numpy(dtype=dtype, na_value=na_value, copy=copy)
        return data

    __array_priority__ = 1000  # higher than ndarray so ops dispatch to us

    def __array__(
        self, dtype: NpDtype | None = None, copy: bool | None = None
    ) -> np.ndarray:
        """
        the array interface, return my values
        We return an object array here to preserve our scalar values
        """
        if copy is False:
            if not self._hasna:
                # special case, here we can simply return the underlying data
                result = np.array(self._data, dtype=dtype, copy=copy)
                # If the ExtensionArray is readonly, make the numpy array readonly too

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Drop or impute missing values first: arr.dropna().astype('bool') or arr.fillna(False).astype('bool').
  2. Convert via to_numpy with an explicit na_value: arr.to_numpy(dtype='bool', na_value=False).
  3. Use a nullable Boolean dtype if NA must be preserved: arr.astype('boolean').

Example fix

// before
s.astype(bool)  # raises: cannot convert float NaN to bool

// after
s.fillna(False).astype(bool)
Defensive patterns

Strategy: validation

Validate before calling

def safe_bool_cast(arr):
    if getattr(arr, "_hasna", False):
        raise ValueError("array has NA; fill or drop before casting to bool")
    return arr.astype(bool)

Type guard

def is_na_free(arr) -> bool:
    return not getattr(arr, "_hasna", False)

Try / catch

try:
    out = arr.astype(bool)
except ValueError as e:
    if "cannot convert float NaN to bool" in str(e):
        out = arr.fillna(False).astype(bool)
    else:
        raise

Prevention

When it happens

Trigger: Calling arr.astype('bool') / arr.astype(bool) / np.asarray(arr, dtype=bool) on a BaseMaskedArray that has any missing values.

Common situations: Converting a nullable numeric column to a boolean mask while the column still has NaNs; building a truth array from a nullable Series for indexing.

Related errors


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