{"record":{"id":"0567ff71ed387b13","repo":"pandas-dev/pandas","slug":"cannot-modify-read-only-array-0567ff","errorCode":null,"errorMessage":"Cannot modify read-only array","messagePattern":"Cannot modify read-only array","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/arrow/array.py","lineNumber":1540,"sourceCode":"\n        result = pc.array_sort_indices(\n            self._pa_array, order=order, null_placement=null_placement\n        )\n        np_result = result.to_numpy()\n        return np_result.astype(np.intp, copy=False)\n\n    def sort(\n        self,\n        *,\n        ascending: bool = True,\n        kind: SortKind = \"quicksort\",\n        na_position: str = \"last\",\n    ) -> None:\n        # This override replaces self._pa_array directly, bypassing __setitem__,\n        # so enforce the read-only guard here to stay consistent with it and\n        # with the base ExtensionArray.sort.\n        if self._readonly:\n            raise ValueError(\"Cannot modify read-only array\")\n        sort_indices = self.argsort(\n            ascending=ascending, kind=kind, na_position=na_position\n        )\n        sorted_array = self.take(sort_indices)\n        self._pa_array = sorted_array._pa_array\n        # Invalidate any cache_readonly properties that depend on _pa_array\n        self._cache.clear()\n\n    def _argmin_max(self, skipna: bool, method: str) -> int:\n        if self._pa_array.length() in (0, self._pa_array.null_count) or (\n            self._hasna and not skipna\n        ):\n            # For empty or all null, pyarrow returns -1 but pandas expects TypeError\n            # For skipna=False and data w/ null, pandas expects NotImplementedError\n            # let ExtensionArray.arg{max|min} raise\n            return getattr(super(), f\"arg{method}\")(skipna=skipna)\n\n        data = self._pa_array","sourceCodeStart":1522,"sourceCodeEnd":1558,"githubUrl":"https://github.com/pandas-dev/pandas/blob/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/arrays/arrow/array.py#L1522-L1558","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nview = big_arr[:100]   # may inherit _readonly\nview.sort()            # ValueError\n# after\nsorted_view = view.copy()\nsorted_view.sort()\n# or non-mutating\norder = view.argsort()\nsorted_view = view.take(order)","handlingStrategy":"validation","validationCode":"from pandas.core.arrays.arrow import ArrowExtensionArray\n\ndef safe_sort_inplace(arr, **kw):\n    if isinstance(arr, ArrowExtensionArray) and getattr(arr, '_readonly', False):\n        arr = arr.copy()\n    arr.sort(**kw)\n    return arr\n\nsorted_arr = safe_sort_inplace(view)","typeGuard":"from pandas.core.arrays.arrow import ArrowExtensionArray\n\ndef is_mutable_arrow_array(arr) -> bool:\n    return not (isinstance(arr, ArrowExtensionArray) and getattr(arr, '_readonly', False))","tryCatchPattern":"try:\n    arr.sort()\nexcept ValueError as e:\n    if 'read-only' in str(e):\n        arr = arr.copy()\n        arr.sort()\n    else:\n        raise","preventionTips":["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."],"tags":["pyarrow","sorting","readonly","mutation-guard"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}