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 replaces self._pa_array directly (bypassing __setitem__), so it explicitly re-checks the read-only guard to stay consistent with ExtensionArray.__setitem__ and base ExtensionArray.sort. Read-only arrays are produced by views into other arrays and must not be mutated.

Solutions

  1. Operate on a copy: arr.copy().sort(...) or use sort_values without inplace.
  2. Detect read-only state before sorting: if getattr(arr, '_readonly', False): arr = arr.copy().
  3. Use argsort + take to get a sorted copy without mutating: sorted_arr = arr.take(arr.argsort()).
  4. Avoid inplace mutations on views; prefer functional style.

Example fix

# before
view = arr.view()
view._readonly = True
view.sort()  # raises ValueError

# after
sorted_arr = view.copy()
sorted_arr.sort()  # or: view.take(view.argsort())
Defensive patterns

Strategy: validation

Validate before calling

def safe_sort(arr, **kw):
    if getattr(arr, '_readonly', False):
        arr = arr.copy()
    arr.sort(**kw)
    return arr

Type guard

def is_readonly(arr) -> bool:
    return bool(getattr(arr, '_readonly', False))

Prevention

When it happens

Trigger: arr.sort() on an array flagged _readonly (e.g. a view returned by another operation, or an array explicitly marked read-only via arr.view()._readonly = True); calling sort_values(inplace=True) on a Series backed by a read-only pyarrow view.

Common situations: Chained operations that hand back a read-only view; libraries that mark arrays read-only to enforce immutability; debugging sessions where the user tries to sort in place after slicing produced a view.

Related errors


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

Appendix: source

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

        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 3b7651241d)