pandas-dev/pandas · error · ValueError

Cannot modify read-only array

Error message

Cannot modify read-only array

What it means

Raised in `IntervalArray.__setitem__` when `self._readonly` is True. Some IntervalArray instances wrap a read-only memory buffer (e.g. views over a parent array, or buffers marked write-protected); any in-place assignment is refused to avoid silently failing or corrupting shared memory.

Solutions

  1. Copy before writing: `arr = arr.copy()` then assign.
  2. Operate at the Series level: `s.iloc[i] = value`, which handles copy semantics.
  3. Identify the source: if the buffer is unexpectedly read-only, rebuild the array from a writable buffer.

Example fix

# before
arr = df['bins'].array  # _readonly True
arr[0] = pd.Interval(0,1)

# after
arr = df['bins'].array.copy()
arr[0] = pd.Interval(0,1)
df['bins'] = arr
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

def is_writable_interval_array(arr) -> bool:
    return not getattr(arr, '_readonly', False)

Try / catch

try:
    arr[i] = value
except ValueError as e:
    if 'Cannot modify read-only array' in str(e):
        arr = arr.copy()
        arr[i] = value
    else:
        raise

Prevention

When it happens

Trigger: Calling `arr[i] = value` on an IntervalArray obtained as a view (`.values` of a Series backed by a read-only buffer, or slices flagged as views); writing to an array derived from a numpy read-only array.

Common situations: Operating on `series.array` after the Series was constructed from a read-only numpy buffer; pickle/IPC round-trips that mark buffers read-only; cython/numba interop.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/152e9b641e635931. Report an issue: GitHub.

Appendix: source

Thrown at pandas/core/arrays/interval.py:696

            # scalar
            if is_scalar(left) and isna(left):
                return self._fill_value
            return Interval(left, right, self.closed)
        if np.ndim(left) > 1:
            # GH#30588 multi-dimensional indexer disallowed
            raise ValueError("multi-dimensional indexing not allowed")
        # Argument 2 to "_simple_new" of "IntervalArray" has incompatible type
        # "Union[Period, Timestamp, Timedelta, NaTType, DatetimeArray, TimedeltaArray,
        # ndarray[Any, Any]]"; expected "Union[Union[DatetimeArray, TimedeltaArray],
        # ndarray[Any, Any]]"
        result = self._simple_new(left, right, dtype=self.dtype)  # type: ignore[arg-type]
        if getitem_returns_view(self, key):
            result._readonly = self._readonly
        return result

    def __setitem__(self, key, value) -> None:
        if self._readonly:
            raise ValueError("Cannot modify read-only array")

        key = check_array_indexer(self, key)
        value_left, value_right = self._validate_setitem_value(value)

        self._left[key] = value_left
        self._right[key] = value_right

    def _cmp_method(self, other, op):
        # ensure pandas array for list-like and eliminate non-interval scalars
        if is_list_like(other):
            if not isinstance(
                other, (list, np.ndarray, ExtensionArray)
            ) and not ops.has_castable_attr(other):
                warnings.warn(
                    f"Operation with {type(other).__name__} is deprecated. "
                    "In a future version these will be treated as scalar-like. "
                    "To retain the old behavior, explicitly wrap in a Series "
                    "instead.",

View on GitHub (pinned to 3b7651241d)