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_arrayView on GitHub (pinned to 71959b8cb9)
Solutions
- Copy before sorting: arr.copy().sort() or use arr.sort_values() (returns new).
- Clear the read-only flag only if you own the array: arr._readonly = False (advanced).
- Use the non-mutating argsort + take pattern to produce a sorted copy.
- 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
- Prefer .sort_values() (returns a new object) over in-place .sort() on views.
- Copy arrays obtained as slices before mutating.
- Check arr._readonly before in-place operations if you own the array.
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
- invalid na_position: {na_position}
- Only np.ndarray, ExtensionArray, and Index objects are allow
- Invalid side: {side}. Side must be one of 'left', 'right', '
- invalid normalization form
- replace is not supported with a re.Pattern, callable repl, c
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/0567ff71ed387b13.
Report an issue: GitHub.