Skip to content

Python · correctness

no-frozen-after-validator-field-write

python:no-frozen-after-validator-field-write

Do not assign declared fields in after-validators on frozen Pydantic models.

Code
SARJ401
Default
error
Fix
none
Languages
python

Why

Direct field assignment contradicts the model's frozen contract and can fail only when the validator runs, making construction behavior surprising and brittle.

Fix

Validate without mutation, compute the value before constructing the model, or return an explicitly updated model when replacement is part of the design.

Before / after

Executed by this rule’s unit tests.

Before

After-validator assigns a frozen model field

app/models.py · focus
from pydantic import BaseModel, ConfigDict, model_validator

class Counter(BaseModel):
    model_config = ConfigDict(frozen=True)
    value: int

    @model_validator(mode="after")
    def normalize(self):
        self.value = abs(self.value)
        return self

After

After-validator checks without mutating the model

app/models.py · focus
from pydantic import BaseModel, ConfigDict, model_validator

class Counter(BaseModel):
    model_config = ConfigDict(frozen=True)
    value: int

    @model_validator(mode="after")
    def require_non_negative(self):
        if self.value < 0:
            raise ValueError("value must be non-negative")
        return self

Limits

  • The rule checks direct public fields on direct Pydantic `BaseModel` subclasses configured with literal `ConfigDict(frozen=True)`.
  • It detects direct assignment, annotated assignment, augmented assignment, and tuple or list destructuring through the validator's receiver.
  • Indirect mutation through method calls, `setattr`, or `object.__setattr__` is outside its scope.
  • Test and generated files are excluded.