ValueError: assignment destination is read-only (NumPy, pandas 3, PIL, tested)
ValueError: assignment destination is read-only means you wrote into a NumPy array whose flags.writeable is False. Common sources: np.frombuffer(bytes), np.broadcast_to, np.asarray(PIL image), and since pandas 3, df[col].to_numpy() and .values. The fix is usually .copy().
1. Read-only arrays from NumPy itself
import numpy as np
a = np.frombuffer(b"\x01\x02\x03", dtype=np.uint8)
print(a.flags.writeable)
try:
a[0] = 9
except ValueError as e:
print("ValueError:", e)
b = np.broadcast_to(np.arange(3), (2, 3))
try:
b[0, 0] = 9
except ValueError as e:
print("ValueError:", e)
c = np.arange(3)
c.flags.writeable = False
try:
c += 1
except ValueError as e:
print("ValueError:", e)
Output:
False
ValueError: assignment destination is read-only
ValueError: assignment destination is read-only
ValueError: output array is read-only
An array is read-only when a.flags.writeable is False. np.frombuffer on immutable bytes, np.broadcast_to (a view that repeats memory), and arrays you lock with flags.writeable = False all refuse writes. In-place operators such as += fail with the related message "output array is read-only".
2. pandas 3: to_numpy() and .values are read-only
import numpy as np
import pandas as pd
df = pd.DataFrame({"x": [1.0, 2.0, 3.0]})
arr = df["x"].to_numpy()
print(pd.__version__, arr.flags.writeable)
try:
arr[0] = 99
print("df after:", df["x"].tolist())
except ValueError as e:
print("ValueError:", e)
vals = df.values
print("df.values writeable:", vals.flags.writeable)
pandas 2.3.3:
2.3.3 True
df after: [99.0, 2.0, 3.0]
df.values writeable: True
pandas 3.1.0:
3.1.0 False
ValueError: assignment destination is read-only
df.values writeable: False
pandas 3 enables Copy-on-Write, so to_numpy() and .values return a read-only view of the column data. On pandas 2 the same write succeeded and silently changed the DataFrame. Code that relied on that now raises. Details on other pandas 3 changes: pandas 3 breaking changes.
import pandas as pd
df = pd.DataFrame({"x": [1.0, 2.0, 3.0]})
arr = df["x"].to_numpy(copy=True) # your own copy
arr[0] = 99
print(arr, df["x"].tolist())
df.loc[0, "x"] = 99 # change the DataFrame itself
print(df["x"].tolist())
Output:
[99. 2. 3.] [1.0, 2.0, 3.0]
[99.0, 2.0, 3.0]
Use to_numpy(copy=True) when you want an array to modify on its own (the DataFrame stays unchanged), or write to the DataFrame with .loc when you want the DataFrame to change.
3. Images: np.asarray(PIL image)
import numpy as np
from PIL import Image
img = Image.new("RGB", (2, 2))
px = np.asarray(img)
print(px.flags.writeable)
try:
px[0, 0] = 255
except ValueError as e:
print("ValueError:", e)
px2 = np.array(img) # np.array copies
px2[0, 0] = 255
print(px2[0, 0])
Output:
False
ValueError: assignment destination is read-only
[255 255 255]
np.asarray on a Pillow image returns a read-only array; np.array(img) makes a writable copy (tested with Pillow 13.0.0).
4. Fixes
import numpy as np
a = np.frombuffer(b"\x01\x02\x03", dtype=np.uint8)
try:
a.setflags(write=True)
except ValueError as e:
print("ValueError:", e)
w = a.copy() # fix 1: copy
w[0] = 9
print(w, w.flags.writeable)
buf = bytearray(b"\x01\x02\x03")
m = np.frombuffer(buf, dtype=np.uint8) # fix 2: mutable buffer
m[0] = 9
print(m, buf)
Output:
ValueError: cannot set WRITEABLE flag to True of this array
[9 2 3] True
[9 2 3] bytearray(b'\t\x02\x03')
setflags(write=True) only works when the underlying memory is writable; for bytes it raises "cannot set WRITEABLE flag to True of this array". Make a copy with .copy() (or np.array(x)), or build the array from a bytearray, which is mutable: writes then go through to the buffer, as the last line shows.
Related: NumPy RuntimeWarning · pandas SettingWithCopyWarning · numpy has no attribute 'int'.
Frequently asked questions
Why is my NumPy array read-only?
It is a view of memory NumPy may not change: immutable bytes (frombuffer), a broadcast view, a PIL image via np.asarray, or pandas 3 data via to_numpy()/.values. Check a.flags.writeable.
Why did to_numpy() become read-only in pandas 3?
pandas 3 uses Copy-on-Write, so to_numpy() returns a read-only view. Use to_numpy(copy=True) for a writable array, or change the DataFrame with .loc.
Can I just call setflags(write=True)?
Only if the underlying memory is writable. For arrays over bytes it raises 'cannot set WRITEABLE flag to True of this array'; use .copy() instead.
Written with AI assistance. Measured on 2026-10-23 in Docker: python:3-alpine (Python 3.14.7) with numpy 2.5.4, Pillow 13.0.0, and pandas 2.3.3 and 3.1.0. Each block was run verbatim and the output pasted unedited. PlainTables is our extension; Python, NumPy, pandas and Pillow are free.