pandas-dev/pandas · error · ValueError
Storage must be 'python' or 'pyarrow'. Got
Error message
Storage must be 'python' or 'pyarrow'. Got {storage} instead. What it means
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.
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.
Example fix
// before
pd.set_option('mode.string_storage', 'arrow')
pd.Series(['a'], dtype='string') # raises ValueError
// after
pd.set_option('mode.string_storage', 'pyarrow')
pd.Series(['a'], dtype='string') Defensive patterns
Strategy: validation
Validate before calling
VALID = {'python', 'pyarrow'}
def safe_string_dtype(storage=None, **kw):
if storage is not None and storage not in VALID:
raise ValueError(f'storage must be one of {VALID}, got {storage!r}')
return pd.StringDtype(storage=storage, **kw) Type guard
def valid_storage(s: str) -> bool:
return s in {'python', 'pyarrow'} Try / catch
null
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- is a python keyword
- is not a valid identifier
- 'na_value' must be np.nan or pd.NA, got
- can only insert Interval objects and NA into an…
- cannot assign without a target object
AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11).
Data as JSON: /api/errors/45fcd0e8b0165258.
Report an issue: GitHub.
Appendix: source
Thrown at pandas/core/arrays/string_.py:212
_metadata = ("storage", "_na_value") # type: ignore[assignment]
def __init__(
self,
storage: str | None = None,
na_value: libmissing.NAType | float = libmissing.NA,
) -> None:
# infer defaults
if storage is None:
storage = config["mode"]["string_storage"]
if storage == "auto":
if HAS_PYARROW:
storage = "pyarrow"
else:
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
View on GitHub (pinned to 3b7651241d)