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

  1. Use exactly 'python' or 'pyarrow': pd.StringDtype(storage='pyarrow').
  2. Reset the global option: pd.set_option('mode.string_storage', 'python') (or 'pyarrow').
  3. 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

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


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)