django/django · error · ValidationError
max_whole_digits
max_whole_digits
Error message
Ensure that there is no more than %(max)s digit before the decimal point.
What it means
Raised by DecimalValidator.__call__ (django/core/validators.py:580) when whole_digits (digits before the decimal point = total digits minus decimals) exceeds (max_digits - decimal_places). This is the integer-portion capacity of the field; it is checked only when both max_digits and decimal_places are set.
Source
Thrown at django/core/validators.py:580
if self.max_digits is not None and digits > self.max_digits:
raise ValidationError(
self.messages["max_digits"],
code="max_digits",
params={"max": self.max_digits, "value": value},
)
if self.decimal_places is not None and decimals > self.decimal_places:
raise ValidationError(
self.messages["max_decimal_places"],
code="max_decimal_places",
params={"max": self.decimal_places, "value": value},
)
if (
self.max_digits is not None
and self.decimal_places is not None
and whole_digits > (self.max_digits - self.decimal_places)
):
raise ValidationError(
self.messages["max_whole_digits"],
code="max_whole_digits",
params={"max": (self.max_digits - self.decimal_places), "value": value},
)
def __eq__(self, other):
return (
isinstance(other, self.__class__)
and self.max_digits == other.max_digits
and self.decimal_places == other.decimal_places
)
@deconstructible
class FileExtensionValidator:
message = _(
"File extension “%(extension)s” is not allowed. "
"Allowed extensions are: %(allowed_extensions)s."View on GitHub (pinned to ae25a40be0)
Solutions
- Increase max_digits (keeping decimal_places) to enlarge the whole-digit allowance, and migrate.
- Reduce the integer magnitude (store in smaller units, or cap input).
- If the value is always integral, use BigIntegerField instead of DecimalField.
Example fix
// before
# DecimalField(max_digits=5, decimal_places=2) -> 3 whole digits
obj.amount = Decimal('1234.56') # 4 whole digits -> rejected
// after
# migrate to DecimalField(max_digits=7, decimal_places=2) -> 5 whole digits
obj.amount = Decimal('1234.56') Defensive patterns
Strategy: validation
Validate before calling
from decimal import Decimal
def fits_whole_digits(value, max_digits, decimal_places) -> bool:
d = Decimal(value)
if not d.is_finite():
return False
digits, exp = d.as_tuple()[1:]
total = len(digits) + (exp if exp > 0 else 0)
decimals = 0 if exp >= 0 else -exp
return (total - decimals) <= (max_digits - decimal_places) Type guard
from decimal import Decimal
def is_within_whole_budget(value, max_digits: int, decimal_places: int) -> bool:
try:
d = Decimal(value)
except Exception:
return False
if not d.is_finite(): return False
digits, exp = d.as_tuple()[1:]
total = len(digits) + max(exp, 0)
decimals = 0 if exp >= 0 else -exp
return (total - decimals) <= (max_digits - decimal_places) Try / catch
from django.core.exceptions import ValidationError
try:
validator(amount)
except ValidationError as e:
if e.code == 'max_whole_digits':
raise ValueError(f'value exceeds whole-digit budget {max_digits - decimal_places}')
raise Prevention
- Plan whole-digit capacity (max_digits - decimal_places) for the largest realistic value.
- Use BigIntegerField for pure integer monetary amounts (store cents).
- Migrate proactively when business scale grows.
When it happens
Trigger: DecimalField(max_digits=5, decimal_places=2) permits 3 whole digits; Decimal('1234.56') has 4 whole digits and is rejected even though total digits (6) would only trip max_digits separately.
Common situations: Field capacity misjudged for the integer magnitude (e.g., a balance field that needs to hold millions); schema designed for small numbers later asked to store large ones; reporting rollups exceeding the per-row capacity.
Related errors
- max_digits
- max_decimal_places
- The %s setting must be a list or a tuple.
- Incorrect timezone setting: %s
- %s cannot be blank.
AI-assisted analysis of django/django@ae25a40be0 (2026-08-06).
Data as JSON: /api/errors/5171e7a34b80a967.
Report an issue: GitHub.