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 an in-place fill (copy=False) is requested on an array whose _readonly flag is set. pandas marks sliced/shared masked arrays read-only to prevent mutating buffers aliased by other arrays, so it refuses to run the Cython backfill/pad routine that writes into self._data in place.
Solutions
- Pass copy=True (the default) so _pad_or_backfill clones data and mask before writing.
- Materialize an independent, writable copy first: arr = arr.copy() then arr.ffill() / arr.bfill().
- Avoid requesting in-place fills on views; reassign the result instead of relying on copy=False.
Example fix
// before arr = parent[start:stop] # view with _readonly=True arr._pad_or_backfill(method="ffill", copy=False) # raises // after arr = parent[start:stop].copy() out = arr._pad_or_backfill(method="ffill", copy=True)
Defensive patterns
Strategy: validation
Validate before calling
if getattr(arr, '_readonly', False):
arr = arr.copy()
out = arr._pad_or_backfill(method='ffill', copy=True) Type guard
def is_writable_masked(arr) -> bool:
return not getattr(arr, '_readonly', False) Try / catch
try:
out = arr._pad_or_backfill(method='ffill', copy=False)
except ValueError as e:
if 'read-only' in str(e):
out = arr.copy()._pad_or_backfill(method='ffill', copy=False)
else:
raise Prevention
- Default to copy=True for any fill/backfill on potentially shared masked arrays.
- Treat masked-array slices as read-only until you explicitly .copy().
- Reassign results of fill/ffill/bfill instead of relying on copy=False.
When it happens
Trigger: Calling arr.ffill(), arr.bfill(), or arr._pad_or_backfill(..., copy=False) on a masked ExtensionArray (Int64, Float64, boolean) that was obtained through a slice/view that propagated _readonly=True, or any array explicitly constructed with read-only buffers.
Common situations: Filling NA values on a Series backed by a slice of a larger masked array; chaining .ffill() after a view-producing operation; libraries that hand back read-only views to enforce immutability; passing copy=False expecting zero-copy behavior.
Related errors
- can only perform ops with 1-d structures
- Cannot cast NaN value to Integer dtype.
- cannot convert float NaN to bool
- cannot convert NA to integer
- cannot convert to ' '-dtype NumPy array with missing…
AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11).
Data as JSON: /api/errors/0424dd786fe16cdd.
Report an issue: GitHub.
Appendix: 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 3b7651241d)