pydantic/pydantic · error · TypeError
"{self.__class__.__name__}" is immutable and does not suppor
Error message
"{self.__class__.__name__}" is immutable and does not support item assignment What it means
Raised by BaseModel.__setattr__ (main.py:382) when assigning to any attribute on a model whose Config has allow_mutation=False or frozen=True. Such models are immutable after construction; any setattr on a non-private, non-dunder attribute is rejected with this TypeError. (Private attributes and DUNDER_ATTRIBUTES bypass this via the earlier branch in __setattr__.)
Source
Thrown at pydantic/v1/main.py:382
raise validation_error
try:
object_setattr(__pydantic_self__, '__dict__', values)
except TypeError as e:
raise TypeError(
'Model values must be a dict; you may not have returned a dictionary from a root validator'
) from e
object_setattr(__pydantic_self__, '__fields_set__', fields_set)
__pydantic_self__._init_private_attributes()
@no_type_check
def __setattr__(self, name, value): # noqa: C901 (ignore complexity)
if name in self.__private_attributes__ or name in DUNDER_ATTRIBUTES:
return object_setattr(self, name, value)
if self.__config__.extra is not Extra.allow and name not in self.__fields__:
raise ValueError(f'"{self.__class__.__name__}" object has no field "{name}"')
elif not self.__config__.allow_mutation or self.__config__.frozen:
raise TypeError(f'"{self.__class__.__name__}" is immutable and does not support item assignment')
elif name in self.__fields__ and self.__fields__[name].final:
raise TypeError(
f'"{self.__class__.__name__}" object "{name}" field is final and does not support reassignment'
)
elif self.__config__.validate_assignment:
new_values = {**self.__dict__, name: value}
for validator in self.__pre_root_validators__:
try:
new_values = validator(self.__class__, new_values)
except (ValueError, TypeError, AssertionError) as exc:
raise ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], self.__class__)
known_field = self.__fields__.get(name, None)
if known_field:
# We want to
# - make sure validators are called without the current value for this field inside `values`
# - keep other values (e.g. submodels) untouched (using `BaseModel.dict()` will change them into dicts)View on GitHub (pinned to 2e5f0e2b42)
Solutions
- If mutation is intended, set Config.frozen=False and allow_mutation=True (or remove frozen=True).
- Use model.copy(update={...}) to produce a new instance with changes.
- For frozen-by-design data, treat instances as values and replace, not mutate.
Example fix
// before
class M(BaseModel):
name: str
class Config:
frozen = True
m = M(name='x')
m.name = 'y' # raises 'is immutable'
// after
m = m.copy(update={'name': 'y'})
# or remove frozen if mutation is desired Defensive patterns
Strategy: type-guard
Validate before calling
def is_mutable(model_cls) -> bool:
cfg = model_cls.__config__
return getattr(cfg, 'allow_mutation', True) and not getattr(cfg, 'frozen', False)
# usage
if not is_mutable(MyModel):
raise TypeError(f'{MyModel.__name__} is frozen; use .copy(update=...)')
m.field = value Type guard
def can_assign(instance) -> bool:
cls = type(instance)
cfg = cls.__config__
return bool(getattr(cfg, 'allow_mutation', True)) and not bool(getattr(cfg, 'frozen', False)) Try / catch
try:
m.field = value
except TypeError as e:
if 'immutable' in str(e):
m = m.copy(update={'field': value})
else:
raise Prevention
- For frozen models, use model.copy(update={...}) instead of mutation.
- Document which models are frozen in module docstrings.
- Add tests that assert immutability for value-type models.
When it happens
Trigger: Setting an attribute on a model declared with frozen=True or allow_mutation=False, including attempts that look like updates inside business logic.
Common situations: Using frozen models for caching/hashability, sharing model instances across threads, and forgetting a model is frozen when porting mutable code.
Related errors
- frozen_instance
- "{self.__class__.__name__}" object has no field "{name}"
- "{self.__class__.__name__}" object "{name}" field is final a
- model-config-invalid-field-name
- config-both
AI-assisted analysis of pydantic/pydantic@2e5f0e2b42 (2026-08-04).
Data as JSON: /data/errors/2f2bdb8a458036c1.json.
Report an issue: GitHub.