{"record":{"id":"8c1e35cfa6d9debf","repo":"pandas-dev/pandas","slug":"na-value-must-be-np-nan-or-pd-na-got-na-value","errorCode":null,"errorMessage":"'na_value' must be np.nan or pd.NA, got {na_value}","messagePattern":"'na_value' must be np\\.nan or pd\\.NA, got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/string_.py","lineNumber":226,"sourceCode":"                    storage = \"python\"\n\n        # validate options\n        if storage not in {\"python\", \"pyarrow\"}:\n            raise ValueError(\n                f\"Storage must be 'python' or 'pyarrow'. Got {storage} instead.\"\n            )\n        if storage == \"pyarrow\" and not HAS_PYARROW:\n            raise ImportError(\n                f\"pyarrow>={PYARROW_MIN_VERSION} is required for PyArrow \"\n                \"backed StringArray.\"\n            )\n\n        if isinstance(na_value, float) and np.isnan(na_value):\n            # when passed a NaN value, always set to np.nan to ensure we use\n            # a consistent NaN value (and we can use `dtype.na_value is np.nan`)\n            na_value = np.nan\n        elif na_value is not libmissing.NA:\n            raise ValueError(f\"'na_value' must be np.nan or pd.NA, got {na_value}\")\n\n        self._storage = cast(\"str\", storage)\n        self._na_value = na_value\n\n    def __repr__(self) -> str:\n        storage = \"\" if self.storage == \"pyarrow\" else \"storage='python', \"\n        return f\"<StringDtype({storage}na_value={self._na_value})>\"\n\n    def __eq__(self, other: object) -> bool:\n        # we need to override the base class __eq__ because na_value (NA or NaN)\n        # cannot be checked with normal `==`\n        if isinstance(other, str):\n            # TODO should dtype == \"string\" work for the NaN variant?\n            if other == \"string\" or other == self.name:  # noqa: PLR1714 (repeated-equality-comparison)\n                return True\n            try:\n                other = self.construct_from_string(other)\n            except (TypeError, ImportError):","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/string_.py#L208-L244","documentation":"Thrown by StringDtype.__init__ in pandas/core/arrays/string_.py:226 when na_value is neither a NaN float nor pandas.NA. StringDtype only supports these two sentinel missing values because its downstream comparisons and is_string_array checks key on identity (na_value is np.nan or na_value is libmissing.NA).","triggerScenarios":"Calling pd.StringDtype(na_value=0), na_value='', na_value=None, or na_value='NA' (string). Constructing a StringDtype with a custom missing-value sentinel.","commonSituations":"User assumes any falsy value can represent missingness. Passing None expecting it to be coerced to NA (None is not is_nan and is not libmissing.NA). Migration from object dtype where None was the missing marker.","solutions":["Use pd.NA (default) for the modern nullable string type: pd.StringDtype() or pd.StringDtype(na_value=pd.NA).","Use np.nan for the NaN-flavored variant: pd.StringDtype(na_value=np.nan).","If a custom sentinel is truly needed, use a Categorical or object dtype instead."],"exampleFix":"// before\npd.StringDtype(na_value='')  # raises ValueError\n\n// after\npd.StringDtype(na_value=pd.NA)","handlingStrategy":"validation","validationCode":"def safe_na_value(v):\n    import numpy as np, pandas as pd\n    if v is pd.NA or (isinstance(v, float) and np.isnan(v)):\n        return v\n    raise ValueError('na_value must be pd.NA or np.nan')","typeGuard":"import numpy as np, pandas as pd\ndef valid_na_value(v) -> bool:\n    return v is pd.NA or (isinstance(v, float) and np.isnan(v))","tryCatchPattern":"null","preventionTips":["Do not invent custom missing sentinels for StringDtype.","Default to pd.NA — omit the na_value argument entirely.","Reserve np.nan for the infer_string-future variant."],"tags":["string-dtype","na-value","validation"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}