{"record":{"id":"222baef740df9415","repo":"django/django","slug":"step-size","errorCode":"step_size","errorMessage":"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.","messagePattern":"Ensure this value is a multiple of step size (.+?), starting from (.+?), e\\.g\\. (.+?), (.+?), (.+?), and so on\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"django/core/validators.py","lineNumber":463,"sourceCode":"        self.offset = offset\n\n    def __call__(self, value):\n        if self.offset is None:\n            super().__call__(value)\n        else:\n            cleaned = self.clean(value)\n            limit_value = (\n                self.limit_value() if callable(self.limit_value) else self.limit_value\n            )\n            if self.compare(cleaned, limit_value):\n                offset = cleaned.__class__(self.offset)\n                params = {\n                    \"limit_value\": limit_value,\n                    \"offset\": offset,\n                    \"valid_value1\": offset + limit_value,\n                    \"valid_value2\": offset + 2 * limit_value,\n                }\n                raise ValidationError(self.message, code=self.code, params=params)\n\n    def compare(self, a, b):\n        offset = 0 if self.offset is None else self.offset\n        return not math.isclose(math.remainder(a - offset, b), 0, abs_tol=1e-9)\n\n\n@deconstructible\nclass MinLengthValidator(BaseValidator):\n    message = ngettext_lazy(\n        \"Ensure this value has at least %(limit_value)d character (it has \"\n        \"%(show_value)d).\",\n        \"Ensure this value has at least %(limit_value)d characters (it has \"\n        \"%(show_value)d).\",\n        \"limit_value\",\n    )\n    code = \"min_length\"\n\n    def compare(self, a, b):","sourceCodeStart":445,"sourceCodeEnd":481,"githubUrl":"https://github.com/django/django/blob/ae25a40be07e8a749edf526df37c93e59d4a22c9/django/core/validators.py#L445-L481","documentation":"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.","triggerScenarios":"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.","commonSituations":"Quantity/pricing tier fields; time-interval pickers; range inputs that snap to a step; HTML <input type=number step=...> wired to a Django form.","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."],"exampleFix":"// before\nstep = 5; offset = 0\nqty = 7                          # rejected (7 % 5 != 0)\n// after\nqty = offset + round((7 - offset)/step) * step   # -> 5","handlingStrategy":"validation","validationCode":"import math\ndef is_on_step(value, step, offset=0) -> bool:\n    if step in (0, None):\n        return True\n    return math.isclose(math.remainder(value - offset, step), 0, abs_tol=1e-9)","typeGuard":"import math\ndef is_step_aligned(value: float, step: float, offset: float = 0) -> bool:\n    return step not in (0, None) and math.isclose(math.remainder(value - offset, step), 0, abs_tol=1e-9)","tryCatchPattern":"from django.core.exceptions import ValidationError\ntry:\n    validator(value)\nexcept ValidationError as e:\n    if e.code == 'step_size':\n        value = offset + round((value - offset) / step) * step\n        validator(value)","preventionTips":["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."],"tags":["validation","numbers","step","django"],"analyzedSha":"ae25a40be07e8a749edf526df37c93e59d4a22c9","analyzedAt":"2026-08-06T21:46:51.801Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}