AttributeError: can't set attribute / property has no setter (tested)

AttributeError: can't set attribute and, since Python 3.11, property 'x' of 'C' object has no setter both mean you assigned to something read-only. We ran every common cause on Python 3.13.5, including two setter mistakes that look right.

1. The property has a getter and no setter

class Temp:
    def __init__(self, c):
        self._c = c

    @property
    def celsius(self):
        return self._c

t = Temp(20)
try:
    t.celsius = 25
except AttributeError as e:
    print(type(e).__name__ + ":", e)

Output:

AttributeError: property 'celsius' of 'Temp' object has no setter

Measured on Python 3.13.5: the message is property 'celsius' of 'Temp' object has no setter. Python 3.11 changed this text; older versions said AttributeError: can't set attribute for the same mistake (not re-tested here, we only ran 3.13). Same bug, same fix.

2. It fails inside __init__ too

A common surprise: you never assign to the property “from outside”, but __init__ does self.celsius = c, which goes through the property:

class Temp:
    def __init__(self, c):
        self.celsius = c          # goes through the property

    @property
    def celsius(self):
        return self._c

try:
    Temp(20)
except AttributeError as e:
    print(type(e).__name__ + ":", e)

Output:

AttributeError: property 'celsius' of 'Temp' object has no setter

Either store into the backing name (self._c = c) or add a setter, as in section 4.

3. namedtuple and frozen dataclasses

from collections import namedtuple
from dataclasses import dataclass

Point = namedtuple("Point", "x y")
p = Point(1, 2)
try:
    p.x = 10
except AttributeError as e:
    print("namedtuple:", e)
print("fix:", p._replace(x=10))

@dataclass(frozen=True)
class Cfg:
    port: int

c = Cfg(80)
try:
    c.port = 8080
except AttributeError as e:
    print(type(e).__name__ + ":", e)

Output:

namedtuple: can't set attribute
fix: Point(x=10, y=2)
FrozenInstanceError: cannot assign to field 'port'

Measured: on 3.13.5 a namedtuple still raises the old wording, can't set attribute, so that's the likely source if you see that exact text on a current Python. Use _replace() to get a modified copy. A @dataclass(frozen=True) raises FrozenInstanceError, which is a subclass of AttributeError (checked: issubclass(FrozenInstanceError, AttributeError) is True); use dataclasses.replace(c, port=8080).

4. The fix: add a setter with the same name

class Temp:
    def __init__(self, c):
        self.celsius = c

    @property
    def celsius(self):
        return self._c

    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("below absolute zero")
        self._c = value

t = Temp(20)
t.celsius = 25
print(t.celsius)

Output:

25

5. Two setter mistakes that don't fix it

Forgetting the decorator: the second def celsius simply replaces the property with a plain function:

class Temp:
    @property
    def celsius(self):
        return self._c

    def celsius(self, value):      # meant as a setter, missing @celsius.setter
        self._c = value

t = Temp()
t._c = 1
print(type(Temp.__dict__["celsius"]).__name__)
try:
    print(type(t.celsius).__name__, t.celsius == t._c)
except Exception as e:
    print(type(e).__name__ + ":", e)

Output:

function
method False

Measured: no error at all, but Temp.celsius is now a plain function, so t.celsius is a bound method, not the value. Naming the setter function differently:

class Temp:
    @property
    def celsius(self):
        return self._c

    @celsius.setter
    def set_celsius(self, value):   # setter under a different name
        self._c = value

t = Temp()
try:
    t.celsius = 5
except AttributeError as e:
    print(type(e).__name__ + ":", e)
t.set_celsius = 5
print(t.celsius)

Output:

AttributeError: property 'celsius' of 'Temp' object has no setter
5

Measured: @celsius.setter returns a new property bound to whatever name the function has. Under set_celsius, the original celsius is still read-only. The setter function must use the same name as the getter.

Frequently asked questions

What does AttributeError: can't set attribute mean?

You assigned to a read-only attribute: a property without a setter (Python 3.10 and earlier wording) or a namedtuple field (still this wording on 3.13.5, which we measured).

What does property object has no setter mean in Python?

Since Python 3.11 that's the message for assigning to a @property that has no @x.setter. Add a setter with the same function name, or assign to the backing attribute instead.

Why does my property fail in __init__?

self.name = value in __init__ goes through the property. We measured it raising the same no-setter error; set self._name directly or add a setter.

How do I change a field of a namedtuple?

You can't in place; use p._replace(x=10), which returns a new tuple. We measured it returning Point(x=10, y=2).

Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5 on Linux: a property without a setter (assigned outside and in __init__), namedtuple and frozen dataclass assignment, a correct setter, and two broken setter variants. Every block was run verbatim and its output pasted from that run. Pre-3.11 wording was not re-tested. PlainHeaders is our extension; everything else here is built-in Python.