python/cpython · error · AttributeError

%r object attribute '__dict__' is read-only

Error message

%r object attribute '__dict__' is read-only

What it means

local.__setattr__ in the pure-Python threading.local implementation special-cases assignments to '__dict__' and raises AttributeError naming the class. The per-thread dict is managed internally via _localimpl/._patch, so replacing it from user code would break thread isolation.

Source

Thrown at Lib/_threading_local.py:105

            raise TypeError("Initialization arguments are not supported")
        self = object.__new__(cls)
        impl = _localimpl()
        impl.localargs = (args, kw)
        impl.locallock = RLock()
        object.__setattr__(self, '_local__impl', impl)
        # We need to create the thread dict in anticipation of
        # __init__ being called, to make sure we don't call it
        # again ourselves.
        impl.create_dict()
        return self

    def __getattribute__(self, name):
        with _patch(self):
            return object.__getattribute__(self, name)

    def __setattr__(self, name, value):
        if name == '__dict__':
            raise AttributeError(
                "%r object attribute '__dict__' is read-only"
                % self.__class__.__name__)
        with _patch(self):
            return object.__setattr__(self, name, value)

    def __delattr__(self, name):
        if name == '__dict__':
            raise AttributeError(
                "%r object attribute '__dict__' is read-only"
                % self.__class__.__name__)
        with _patch(self):
            return object.__delattr__(self, name)


from threading import current_thread, RLock

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Mutate the contents instead: for k in list(obj.__dict__): delattr(obj, k)
  2. Give the local subclass an explicit reset() method that deletes known attributes
  3. Use vars(obj) (read-only view of the per-thread dict) when you only need inspection

Example fix

# before
loc.__dict__ = {}  # AttributeError: 'local' object attribute '__dict__' is read-only

# after
for k in list(vars(loc)):
    delattr(loc, k)
Defensive patterns

Strategy: try-catch

Validate before calling

def clear_local(loc):
    for k in list(vars(loc)):
        delattr(loc, k)

Type guard

def supports_dict_rebind(obj) -> bool:
    import threading
    return not isinstance(obj, threading.local)

Try / catch

try:
    obj.__dict__ = new_dict
except AttributeError:
    for k in list(vars(obj)):
        delattr(obj, k)
    for k, v in new_dict.items():
        setattr(obj, k, v)

Prevention

When it happens

Trigger: instance.__dict__ = {} or instance.__dict__['key'] = value via setattr-style assignment on a _threading_local.local (or subclass) instance.

Common situations: Generic plumbing code that 'clears' objects by rebinding obj.__dict__ = {} (works for normal objects, fails here); deepcopy/pickle helpers that manipulate __dict__ directly.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/3fb6cdfeb676ebb4. Report an issue: GitHub.