{"record":{"id":"45fcd0e8b0165258","repo":"pandas-dev/pandas","slug":"storage-must-be-python-or-pyarrow-got-storag","errorCode":null,"errorMessage":"Storage must be 'python' or 'pyarrow'. Got {storage} instead.","messagePattern":"Storage must be 'python' or 'pyarrow'\\. Got (.+?) instead\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/string_.py","lineNumber":212,"sourceCode":"    _metadata = (\"storage\", \"_na_value\")  # type: ignore[assignment]\n\n    def __init__(\n        self,\n        storage: str | None = None,\n        na_value: libmissing.NAType | float = libmissing.NA,\n    ) -> None:\n        # infer defaults\n        if storage is None:\n            storage = config[\"mode\"][\"string_storage\"]\n            if storage == \"auto\":\n                if HAS_PYARROW:\n                    storage = \"pyarrow\"\n                else:\n                    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","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/string_.py#L194-L230","documentation":"Thrown by StringDtype.__init__ in pandas/core/arrays/string_.py:212 when the `storage` argument resolves to anything other than 'python' or 'pyarrow'. StringDtype only has two backing implementations; an unknown storage string is rejected before any state is set.","triggerScenarios":"Calling pd.StringDtype(storage='numpy'), storage='arrow' (typo — must be 'pyarrow'), or storage='auto' as an explicit argument. Also triggered when the global option mode.string_storage is misconfigured to an invalid value, since None storage reads that option.","commonSituations":"User sets pd.set_option('mode.string_storage', 'arrow') (should be 'pyarrow') then creates any string dtype. Passing a custom backend name hoping pandas supports it. Stale option from an old pandas version that accepted different spellings.","solutions":["Use exactly 'python' or 'pyarrow': pd.StringDtype(storage='pyarrow').","Reset the global option: pd.set_option('mode.string_storage', 'python') (or 'pyarrow').","Leave storage=None to let pandas pick the configured default automatically."],"exampleFix":"// before\npd.set_option('mode.string_storage', 'arrow')\npd.Series(['a'], dtype='string')  # raises ValueError\n\n// after\npd.set_option('mode.string_storage', 'pyarrow')\npd.Series(['a'], dtype='string')","handlingStrategy":"validation","validationCode":"VALID = {'python', 'pyarrow'}\ndef safe_string_dtype(storage=None, **kw):\n    if storage is not None and storage not in VALID:\n        raise ValueError(f'storage must be one of {VALID}, got {storage!r}')\n    return pd.StringDtype(storage=storage, **kw)","typeGuard":"def valid_storage(s: str) -> bool:\n    return s in {'python', 'pyarrow'}","tryCatchPattern":"null","preventionTips":["Only set mode.string_storage to 'python' or 'pyarrow'.","Avoid passing storage='auto' explicitly; let None resolve it.","Validate config values at application startup, not at first dtype construction."],"tags":["string-dtype","storage","config","validation"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}