pandas-dev/pandas · error · AttributeError

Can only use the '.struct' accessor with 'struct[pyarrow]'…

Error message

Can only use the '.struct' accessor with 'struct[pyarrow]' dtype, not {dtype}.

What it means

`AttributeError` from `ArrowAccessor._validate` for the `.struct` accessor. It fires when the Series dtype is not an `ArrowDtype` over a pyarrow struct type (`pa.types.is_struct` returns False). Like the `.list` case it uses `AttributeError` so the accessor is hidden from non-struct Series (correct `hasattr` / tab-completion behavior).

Solutions

  1. Construct/convert with an explicit struct dtype: `s.astype(pd.ArrowDtype(pa.struct([(k, pa.int64()) for k in keys])))`.
  2. If loading from records, use `pd.array(records, dtype=pd.ArrowDtype(pa.struct(...)))`.
  3. Guard with `isinstance(s.dtype, pd.ArrowDtype) and pa.types.is_struct(s.dtype.pyarrow_dtype)`.

Example fix

// before
s = pd.Series([{'a':1},{'a':2}])
s.struct.field('a')   # object dtype -> AttributeError

// after
import pyarrow as pa
s = pd.Series([{'a':1},{'a':2}], dtype=pd.ArrowDtype(pa.struct([('a', pa.int64())])))
s.struct.field('a')
Defensive patterns

Strategy: type-guard

Validate before calling

import pyarrow as pa
from pandas.core.dtypes.dtypes import ArrowDtype
if not (isinstance(s.dtype, ArrowDtype) and pa.types.is_struct(s.dtype.pyarrow_dtype)):
    s = s.astype(pd.ArrowDtype(pa.struct([(k, pa.int64()) for k in keys])))
s.struct.field('a')

Type guard

def is_arrow_struct_series(s) -> bool:
    import pyarrow as pa
    from pandas.core.dtypes.dtypes import ArrowDtype
    return isinstance(s.dtype, ArrowDtype) and pa.types.is_struct(s.dtype.pyarrow_dtype)

Try / catch

try:
    out = s.struct.field('a')
except AttributeError as e:
    if '.struct' in str(e):
        s = s.astype(pd.ArrowDtype(pa.struct([('a', pa.int64')])))
        out = s.struct.field('a')
    else:
        raise

Prevention

When it happens

Trigger: `s.struct.field('x')` / `s.struct.explode()` where `s.dtype` is object/dict-of-dict, an Arrow non-struct type (`string[pyarrow]`, `int64[pyarrow]`), or a non-Arrow dtype.

Common situations: Loading JSON/parquet into a Series of dicts and forgetting to cast to a struct Arrow dtype; field-access code that assumes pyarrow storage; dtype silently downcast.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/b1ec3e278611f03d. Report an issue: GitHub.

Appendix: source

Thrown at pandas/core/arrays/arrow/accessors.py:52

    @abstractmethod
    def __init__(self, data, validation_msg: str) -> None:
        self._data = data
        self._validation_msg = validation_msg
        self._validate(data)

    @abstractmethod
    def _is_valid_pyarrow_dtype(self, pyarrow_dtype) -> bool:
        pass

    def _validate(self, data) -> None:
        dtype = data.dtype
        if not HAS_PYARROW or not isinstance(dtype, ArrowDtype):
            # Raise AttributeError so that inspect can handle non-struct Series.
            raise AttributeError(self._validation_msg.format(dtype=dtype))

        if not self._is_valid_pyarrow_dtype(dtype.pyarrow_dtype):
            # Raise AttributeError so that inspect can handle invalid Series.
            raise AttributeError(self._validation_msg.format(dtype=dtype))

    @property
    def _pa_array(self):
        return self._data.array._pa_array


class ListAccessor(ArrowAccessor):
    """
    Accessor object for list data properties of the Series values.

    Parameters
    ----------
    data : Series
        Series containing Arrow list data.
    """

    def __init__(self, data=None) -> None:
        super().__init__(

View on GitHub (pinned to 3b7651241d)