{"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/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/arrays/string_.py#L208-L244","documentation":"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.","triggerScenarios":"Calling StringDtype(na_value=None), StringDtype(na_value=''), StringDtype(na_value=0), StringDtype(na_value='<NA>'), or passing a custom missing-value sentinel.","commonSituations":"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.","solutions":["Use pd.NA (default, pandas nullable semantics) or np.nan (NumPy semantics).","If you need a custom sentinel, store it as a regular string and handle detection in your own logic rather than via na_value.","Do not pass na_value at all to accept the default pd.NA."],"exampleFix":"// before\ndtype = pd.StringDtype(na_value=None)\n\n// after\ndtype = pd.StringDtype(na_value=pd.NA)","handlingStrategy":"validation","validationCode":"import numpy as np\nimport pandas as pd\n\nif na_value is not pd.NA and not (isinstance(na_value, float) and np.isnan(na_value)):\n    na_value = pd.NA\ndtype = pd.StringDtype(na_value=na_value)","typeGuard":"import numpy as np\nimport pandas as pd\n\ndef is_valid_na_value(v) -> bool:\n    return v is pd.NA or (isinstance(v, float) and np.isnan(v))","tryCatchPattern":null,"preventionTips":["Only use pd.NA or np.nan for na_value.","Do not pass na_value at all to accept the pd.NA default.","Keep custom sentinels as regular data, not as na_value."],"tags":["string-dtype","na-value","validation"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}