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

  1. Increase max_digits (keeping decimal_places) to enlarge the whole-digit allowance, and migrate.
  2. Reduce the integer magnitude (store in smaller units, or cap input).
  3. 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

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


AI-assisted analysis of django/django@ae25a40be0 (2026-08-06). Data as JSON: /api/errors/5171e7a34b80a967. Report an issue: GitHub.