django/django · error · ValidationError
step_size
step_size
Error message
Ensure this value is a multiple of step size %(limit_value)s, starting from %(offset)s, e.g. %(offset)s, %(valid_value1)s, %(valid_value2)s, and so on.
What it means
Raised by StepValueValidator.__call__ (django/core/validators.py:463) when offset is set and compare returns True. compare uses math.isclose(math.remainder(value - offset, step), 0, abs_tol=1e-9): the value must be an exact multiple of step measured from offset. The message lists example valid values (offset, offset+step, offset+2*step). Used by IntegerField/DecimalField when the form/widget declares a step_size.
Source
Thrown at django/core/validators.py:463
self.offset = offset
def __call__(self, value):
if self.offset is None:
super().__call__(value)
else:
cleaned = self.clean(value)
limit_value = (
self.limit_value() if callable(self.limit_value) else self.limit_value
)
if self.compare(cleaned, limit_value):
offset = cleaned.__class__(self.offset)
params = {
"limit_value": limit_value,
"offset": offset,
"valid_value1": offset + limit_value,
"valid_value2": offset + 2 * limit_value,
}
raise ValidationError(self.message, code=self.code, params=params)
def compare(self, a, b):
offset = 0 if self.offset is None else self.offset
return not math.isclose(math.remainder(a - offset, b), 0, abs_tol=1e-9)
@deconstructible
class MinLengthValidator(BaseValidator):
message = ngettext_lazy(
"Ensure this value has at least %(limit_value)d character (it has "
"%(show_value)d).",
"Ensure this value has at least %(limit_value)d characters (it has "
"%(show_value)d).",
"limit_value",
)
code = "min_length"
def compare(self, a, b):View on GitHub (pinned to ae25a40be0)
Solutions
- Submit a value equal to offset + n*step (n integer).
- Snap the input to the nearest valid step before submission: rounded = offset + round((value-offset)/step)*step.
- Remove or widen step_size on the field/widget if arbitrary values are acceptable.
Example fix
// before step = 5; offset = 0 qty = 7 # rejected (7 % 5 != 0) // after qty = offset + round((7 - offset)/step) * step # -> 5
Defensive patterns
Strategy: validation
Validate before calling
import math
def is_on_step(value, step, offset=0) -> bool:
if step in (0, None):
return True
return math.isclose(math.remainder(value - offset, step), 0, abs_tol=1e-9) Type guard
import math
def is_step_aligned(value: float, step: float, offset: float = 0) -> bool:
return step not in (0, None) and math.isclose(math.remainder(value - offset, step), 0, abs_tol=1e-9) Try / catch
from django.core.exceptions import ValidationError
try:
validator(value)
except ValidationError as e:
if e.code == 'step_size':
value = offset + round((value - offset) / step) * step
validator(value) Prevention
- Snap inputs to the step grid in the form/widget before submission.
- Document the offset+step contract in help_text.
- Remove step_size when arbitrary values are valid.
When it happens
Trigger: A field with step_size=5 and offset=0 given value 7 (valid: 0,5,10,15); step_size=0.25, offset=0, value 0.30. Both trip the remainder check.
Common situations: Quantity/pricing tier fields; time-interval pickers; range inputs that snap to a step; HTML <input type=number step=...> wired to a Django form.
Related errors
- The %s setting must be a list or a tuple.
- Incorrect timezone setting: %s
- %s cannot be blank.
- password_too_short
- %s requires at least %d points, got %s.
AI-assisted analysis of django/django@ae25a40be0 (2026-08-06).
Data as JSON: /api/errors/222baef740df9415.
Report an issue: GitHub.