pydantic/monty · error · ValueError

Exceeds the limit ({INT_MAX_STR_DIGITS} digits) for integer

Error message

Exceeds the limit ({INT_MAX_STR_DIGITS} digits) for integer string conversion: value has {digit_count} digits; use sys.set_int_max_str_digits() to increase the limit

What it means

Monty's JSON loader (`crates/monty/src/modules/json/load.rs:340`, in `json_number_out_of_range_to_run_error`, reached via `parse_json_bytes`) enforces CPython's `sys.set_int_max_str_digits` limit (4300 digits) when a JSON number literal is a decimal integer token with too many digits. It raises Python's `ValueError` with CPython's exact message, so `json.loads('1234...')` fails identically to CPython instead of producing an arbitrarily huge int. Float/exponent tokens are exempt (they parse as float); only plain integer literals are limited.

Source

Thrown at crates/monty/src/modules/json/load.rs:340

        }));
    }
    Ok(())
}

/// Converts `jiter`'s oversized-integer parse error into CPython's digit-limit
/// `ValueError` when the offending token is a decimal integer literal.
fn json_number_out_of_range_to_run_error(error: &JiterError, bytes: &[u8]) -> Option<RunError> {
    if error.error_type != JiterErrorType::JsonError(JsonErrorType::NumberOutOfRange) {
        return None;
    }

    let token = slice_json_number_around(bytes, error.index);
    if !is_json_integer_token(token) {
        return None;
    }

    let digit_count = decimal_digit_count_ascii(token);
    check_decimal_digit_count(digit_count).err()
}

/// Returns whether a raw JSON number token is an integer literal rather than a
/// float or exponent form.
fn is_json_integer_token(token: &[u8]) -> bool {
    !token.is_empty() && !token.contains(&b'.') && !token.contains(&b'e') && !token.contains(&b'E')
}

/// Returns the JSON number token that surrounds `index`.
///
/// `jiter` reports `NumberOutOfRange` at or just after the failing position, so
/// this scans outward to recover the original token for CPython-compatible
/// integer digit-limit handling.
fn slice_json_number_around(bytes: &[u8], index: usize) -> &[u8] {
    let mut start = index.min(bytes.len());
    while start > 0 && is_json_number_byte(bytes[start - 1]) {
        start -= 1;
    }

View on GitHub (pinned to adc986b362)

Solutions

  1. In sandboxed Python code, call `sys.set_int_max_str_digits(0)` (disable) or a higher limit before `json.loads`.
  2. Quote the value as a JSON string (e.g. `"123456789..."`) and convert with `int(s)` semantics as needed — string keys/IDs should never be bare numbers.
  3. Strip leading zeros or truncate the literal in preprocessing so it stays under the digit limit.
  4. If the huge number is legitimate data, decode it as a float by adding `.0` or an exponent (float tokens bypass the int digit limit).

Example fix

// before
const data = JSON.parse(hugeIntJson);

// after
import sys
if hasattr(sys, 'set_int_max_str_digits'):
    sys.set_int_max_str_digits(0)
data = json.loads(huge_int_json)
Defensive patterns

Strategy: validation

Validate before calling

import sys

def json_safe(obj):
    if isinstance(obj, str) and obj.isdigit() and len(obj) > sys.get_int_max_str_digits():
        raise ValueError('integer literal exceeds int_max_str_digits; pass it as a string')
    return obj

def check_json_ints(text):
    import json
    for m in __import__('re').finditer(r'(?<![.eE\d])\d+', text):
        if len(m.group()) > (sys.get_int_max_str_digits() or 4300):
            raise ValueError(f'JSON integer literal has {len(m.group())} digits')

Try / catch

import sys
try:
    data = json.loads(raw)
except ValueError as exc:
    if 'set_int_max_str_digits' in str(exc):
        sys.set_int_max_str_digits(0)
        data = json.loads(raw)
    else:
        raise

Prevention

When it happens

Trigger: Calling `json.loads()` (or the bytes variant) on input containing an integer literal with more digits than `sys.get_int_max_str_digits()` (default 4300), e.g. a long numeric ID, key, or hash embedded as a bare JSON number.

Common situations: Parsing API responses or data dumps where 64-bit+ IDs were serialized as JSON numbers and later padded with zeros or concatenated, pushing the literal past 4300 digits; code that worked on other engines but must match CPython's int-str conversion limit.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/4baaaa43e7eb9118. Report an issue: GitHub.