pandas-dev/pandas · error · ValueError

Cannot modify read-only array

Error message

Cannot modify read-only array

What it means

Raised by ArrowExtensionArray.sort when self._readonly is True. The override bypasses __setitem__ to replace self._pa_array directly, so it re-checks the read-only flag for consistency. A read-only array is one explicitly marked immutable (e.g. a view returned from __getitem__ where the parent is read-only, or set via internal mechanisms). Mutation in place is forbidden.

Source

Thrown at pandas/core/arrays/arrow/array.py:1540

        result = pc.array_sort_indices(
            self._pa_array, order=order, null_placement=null_placement
        )
        np_result = result.to_numpy()
        return np_result.astype(np.intp, copy=False)

    def sort(
        self,
        *,
        ascending: bool = True,
        kind: SortKind = "quicksort",
        na_position: str = "last",
    ) -> None:
        # This override replaces self._pa_array directly, bypassing __setitem__,
        # so enforce the read-only guard here to stay consistent with it and
        # with the base ExtensionArray.sort.
        if self._readonly:
            raise ValueError("Cannot modify read-only array")
        sort_indices = self.argsort(
            ascending=ascending, kind=kind, na_position=na_position
        )
        sorted_array = self.take(sort_indices)
        self._pa_array = sorted_array._pa_array
        # Invalidate any cache_readonly properties that depend on _pa_array
        self._cache.clear()

    def _argmin_max(self, skipna: bool, method: str) -> int:
        if self._pa_array.length() in (0, self._pa_array.null_count) or (
            self._hasna and not skipna
        ):
            # For empty or all null, pyarrow returns -1 but pandas expects TypeError
            # For skipna=False and data w/ null, pandas expects NotImplementedError
            # let ExtensionArray.arg{max|min} raise
            return getattr(super(), f"arg{method}")(skipna=skipna)

        data = self._pa_array

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Copy before sorting: arr.copy().sort() or use arr.sort_values() (returns new).
  2. Clear the read-only flag only if you own the array: arr._readonly = False (advanced).
  3. Use the non-mutating argsort + take pattern to produce a sorted copy.
  4. Prefer sort_values() at the Series level which returns a new object.

Example fix

# before
view = big_arr[:100]   # may inherit _readonly
view.sort()            # ValueError
# after
sorted_view = view.copy()
sorted_view.sort()
# or non-mutating
order = view.argsort()
sorted_view = view.take(order)
Defensive patterns

Strategy: validation

Validate before calling

from pandas.core.arrays.arrow import ArrowExtensionArray

def safe_sort_inplace(arr, **kw):
    if isinstance(arr, ArrowExtensionArray) and getattr(arr, '_readonly', False):
        arr = arr.copy()
    arr.sort(**kw)
    return arr

sorted_arr = safe_sort_inplace(view)

Type guard

from pandas.core.arrays.arrow import ArrowExtensionArray

def is_mutable_arrow_array(arr) -> bool:
    return not (isinstance(arr, ArrowExtensionArray) and getattr(arr, '_readonly', False))

Try / catch

try:
    arr.sort()
except ValueError as e:
    if 'read-only' in str(e):
        arr = arr.copy()
        arr.sort()
    else:
        raise

Prevention

When it happens

Trigger: Calling `.sort(...)` (in-place) on a read-only ArrowExtensionArray: a slice/view of another array that propagated the _readonly flag, or an array explicitly pinned read-only for safety. `view = arr[:5]; view.sort()` if arr is read-only.

Common situations: Chaining `.sort()` on a slice returned by an operation that sets _readonly; shared/immutable backing arrays; defensive code that marks arrays read-only and then pipelines a sort.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/0567ff71ed387b13. Report an issue: GitHub.