{"record":{"id":"0424dd786fe16cdd","repo":"pandas-dev/pandas","slug":"cannot-modify-read-only-array-0424dd","errorCode":null,"errorMessage":"Cannot modify read-only array","messagePattern":"Cannot modify read-only array","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/masked.py","lineNumber":259,"sourceCode":"        *,\n        method: FillnaOptions,\n        limit: int | None = None,\n        limit_area: Literal[\"inside\", \"outside\"] | None = None,\n        copy: bool = True,\n    ) -> Self:\n        mask = self._mask\n\n        if mask.any():\n            func = missing.get_fill_func(method, ndim=self.ndim)\n\n            npvalues = self._data.T\n            new_mask = mask.T\n            if copy:\n                npvalues = npvalues.copy()\n                new_mask = new_mask.copy()\n            else:\n                if self._readonly:\n                    raise ValueError(\"Cannot modify read-only array\")\n                if limit_area is not None:\n                    mask = mask.copy()\n            func(npvalues, limit=limit, mask=new_mask)\n\n            if limit_area is not None and not mask.all():\n                mask = mask.T\n                neg_mask = ~mask\n                first = neg_mask.argmax()\n                last = len(neg_mask) - neg_mask[::-1].argmax() - 1\n                if limit_area == \"inside\":\n                    new_mask[:first] |= mask[:first]\n                    new_mask[last + 1 :] |= mask[last + 1 :]\n                elif limit_area == \"outside\":\n                    new_mask[first + 1 : last] |= mask[first + 1 : last]\n\n            if copy:\n                return self._simple_new(npvalues.T, new_mask.T)\n            else:","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/pandas-dev/pandas/blob/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/arrays/masked.py#L241-L277","documentation":"Raised by BaseMaskedArray._pad_or_backfill when copy=False is requested on an array whose backing _data is read-only (self._readonly == True). The in-place fill path (ffill/bfill/pad/backfill with limit_area) cannot mutate read-only buffers, so it refuses before any partial write.","triggerScenarios":"Calling arr.ffill(inplace-ish via copy=False), Series.ffill() where the underlying Block is backed by a read-only masked array (e.g. zero-copy from Arrow or shared memory).","commonSituations":"Arrow-backed nullable columns converted with convert_dtypes(dtype_backend='pyarrow'), memory-mapped or pickled read-only data, copy-on-write pipelines.","solutions":["Force a copy first: arr = arr.copy() then arr._pad_or_backfill(method='ffill', copy=False).","Use the public Series API which defaults copy=True: s.ffill().","Check arr._readonly and switch to a copy-on-write path when True."],"exampleFix":"# before\narr._pad_or_backfill(method='ffill', copy=False)  # arr._readonly == True\n\n# after\narr = arr.copy()\narr._pad_or_backfill(method='ffill', copy=False)","handlingStrategy":"validation","validationCode":"def writable_or_copy(arr):\n    if getattr(arr, '_readonly', False):\n        return arr.copy()\n    return arr","typeGuard":"def is_writable_masked(arr) -> bool:\n    return not getattr(arr, '_readonly', False) and arr._data.flags.writeable","tryCatchPattern":null,"preventionTips":["Default to copy=True for fill operations on potentially shared buffers.","Detect Arrow-backed or memmap-backed columns and copy before in-place fill.","Document which sources produce read-only masked arrays in your pipeline."],"tags":["masked-array","readonly","fill","mutation"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}