pandas-dev/pandas · error · ValueError

Cannot modify read-only array

Error message

Cannot modify read-only array

What it means

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.

Source

Thrown at pandas/core/arrays/masked.py:259

        *,
        method: FillnaOptions,
        limit: int | None = None,
        limit_area: Literal["inside", "outside"] | None = None,
        copy: bool = True,
    ) -> Self:
        mask = self._mask

        if mask.any():
            func = missing.get_fill_func(method, ndim=self.ndim)

            npvalues = self._data.T
            new_mask = mask.T
            if copy:
                npvalues = npvalues.copy()
                new_mask = new_mask.copy()
            else:
                if self._readonly:
                    raise ValueError("Cannot modify read-only array")
                if limit_area is not None:
                    mask = mask.copy()
            func(npvalues, limit=limit, mask=new_mask)

            if limit_area is not None and not mask.all():
                mask = mask.T
                neg_mask = ~mask
                first = neg_mask.argmax()
                last = len(neg_mask) - neg_mask[::-1].argmax() - 1
                if limit_area == "inside":
                    new_mask[:first] |= mask[:first]
                    new_mask[last + 1 :] |= mask[last + 1 :]
                elif limit_area == "outside":
                    new_mask[first + 1 : last] |= mask[first + 1 : last]

            if copy:
                return self._simple_new(npvalues.T, new_mask.T)
            else:

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Force a copy first: arr = arr.copy() then arr._pad_or_backfill(method='ffill', copy=False).
  2. Use the public Series API which defaults copy=True: s.ffill().
  3. Check arr._readonly and switch to a copy-on-write path when True.

Example fix

# before
arr._pad_or_backfill(method='ffill', copy=False)  # arr._readonly == True

# after
arr = arr.copy()
arr._pad_or_backfill(method='ffill', copy=False)
Defensive patterns

Strategy: validation

Validate before calling

def writable_or_copy(arr):
    if getattr(arr, '_readonly', False):
        return arr.copy()
    return arr

Type guard

def is_writable_masked(arr) -> bool:
    return not getattr(arr, '_readonly', False) and arr._data.flags.writeable

Prevention

When it happens

Trigger: 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).

Common situations: Arrow-backed nullable columns converted with convert_dtypes(dtype_backend='pyarrow'), memory-mapped or pickled read-only data, copy-on-write pipelines.

Related errors


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