pandas-dev/pandas · error · ValueError

'na_value' must be np.nan or pd.NA, got {na_value}

Error message

'na_value' must be np.nan or pd.NA, got {na_value}

What it means

StringDtype.__init__ requires na_value to be exactly np.nan or pandas.NA (libmissing.NA). A float NaN is normalized to np.nan; anything else (None, '', 0, a custom sentinel) raises ValueError. The na_value determines the missing-value semantics (NaN vs NA) for the entire dtype.

Source

Thrown at pandas/core/arrays/string_.py:226

                    storage = "python"

        # validate options
        if storage not in {"python", "pyarrow"}:
            raise ValueError(
                f"Storage must be 'python' or 'pyarrow'. Got {storage} instead."
            )
        if storage == "pyarrow" and not HAS_PYARROW:
            raise ImportError(
                f"pyarrow>={PYARROW_MIN_VERSION} is required for PyArrow "
                "backed StringArray."
            )

        if isinstance(na_value, float) and np.isnan(na_value):
            # when passed a NaN value, always set to np.nan to ensure we use
            # a consistent NaN value (and we can use `dtype.na_value is np.nan`)
            na_value = np.nan
        elif na_value is not libmissing.NA:
            raise ValueError(f"'na_value' must be np.nan or pd.NA, got {na_value}")

        self._storage = cast("str", storage)
        self._na_value = na_value

    def __repr__(self) -> str:
        storage = "" if self.storage == "pyarrow" else "storage='python', "
        return f"<StringDtype({storage}na_value={self._na_value})>"

    def __eq__(self, other: object) -> bool:
        # we need to override the base class __eq__ because na_value (NA or NaN)
        # cannot be checked with normal `==`
        if isinstance(other, str):
            # TODO should dtype == "string" work for the NaN variant?
            if other == "string" or other == self.name:  # noqa: PLR1714 (repeated-equality-comparison)
                return True
            try:
                other = self.construct_from_string(other)
            except (TypeError, ImportError):

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Use pd.NA (default, pandas nullable semantics) or np.nan (NumPy semantics).
  2. If you need a custom sentinel, store it as a regular string and handle detection in your own logic rather than via na_value.
  3. Do not pass na_value at all to accept the default pd.NA.

Example fix

// before
dtype = pd.StringDtype(na_value=None)

// after
dtype = pd.StringDtype(na_value=pd.NA)
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np
import pandas as pd

if na_value is not pd.NA and not (isinstance(na_value, float) and np.isnan(na_value)):
    na_value = pd.NA
dtype = pd.StringDtype(na_value=na_value)

Type guard

import numpy as np
import pandas as pd

def is_valid_na_value(v) -> bool:
    return v is pd.NA or (isinstance(v, float) and np.isnan(v))

Prevention

When it happens

Trigger: Calling StringDtype(na_value=None), StringDtype(na_value=''), StringDtype(na_value=0), StringDtype(na_value='<NA>'), or passing a custom missing-value sentinel.

Common situations: Assuming None is an acceptable missing-value marker; trying to use a domain-specific sentinel like -1 or '' for missing strings; copy-pasting na_value from another dtype's config.

Related errors


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