googleapis / googleapis/python-genai
ValueError from Python's int_max_str_digits limit escapes generate_content() when the model emits an oversized integer (only JSONDecodeError is guarded in _from_response)
- Dominant language
- Python
- Stars
- 4k
- Forks
- 1k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 40
Description
**Environment**
- python-genai: 1.73.1 (bug still present in 2.7.0, see line refs below)
- Python: 3.14.3 (any Python ≥ 3.11 with the default `int_max_str_digits=4300` is affected)
- Model: `gemini-3-flash-preview`
**Description**
When `response_schema` / `response_json_schema` is set, `GenerateContentResponse._from_response` eagerly parses the response text to populate `.parsed`:
```python
# google/genai/types.py (v2.7.0, lines 8255, 8282; also 8238, 8271 for the pydantic branches)
try:
result_text = result._get_text()
if result_text is not None:
result.parsed = json.loads(result_text)
# may not be a valid json per stream response
except json.decoder.JSONDecodeError:
pass
```
`json.loads` can raise more than `JSONDecodeError`: since Python 3.11 (CVE-2020-10735 mitigation), parsing an integer literal longer than `sys.get_int_max_str_digits()` (default 4300) raises a plain `ValueError`:
```
ValueError: Exceeds the limit (4300 digits) for integer string conversion: value has 25410 digits; use sys.set_int_max_str_digits() to increase the limit
```
This is **not** a subclass of `JSONDecodeError`, so it escapes the guard and propagates out of `generate_content()` — after the API already returned HTTP 200. The caller cannot distinguish this from an SDK/transport failure, and there is no way to access the response (e.g. `.text`) because the exception is raised before the response object is returned.
**Reproduction**
We hit this in production: the model occasionally emits a degenerate/runaway integer literal in structured output (25,410 digits in our case). The application can't prevent that — it's model output. Minimal demonstration of the parsing gap:
```python
import json
try:
json.loads('{"count": ' + "1" * 5000 + "}")
except json.JSONDecodeError:
print("handled") # <- never reached
except ValueError as e:
print(f"escapes: {e}") # <- this is what propagates out of generate_content()
```
Production stack trace (python-genai 1.73.1):
```
ValueError: Exceeds the limit (4300 digits) for integer string conversion: value has 25410 digits; use sys.set_int_max_str_digits() to increase the limit
at raw_decode (json/decoder.py:361)
at decode (json/decoder.py:345)
at loads (json/__init__.py:352)
at _from_response (google/genai/types.py:8172)
at _generate_content (google/genai/models.py:4753)
at generate_content (google/genai/models.py:6276)
```
**Expected behavior**
An unparseable response body (for whatever reason) should degrade the same way a malformed-JSON response does: `.parsed` stays `None` and the caller gets the response object back, with `.text` available for its own error handling.
**Suggested fix**
`json.JSONDecodeError` is a subclass of `ValueError`, so widening the four `except json.decoder.JSONDecodeError:` clauses in `_from_response` to `except ValueError:` keeps the existing behavior and also covers the int-limit case (lines 8238, 8255, 8271, 8282 in v2.7.0).
Contributor guide
Assessment
This issue has not been assessed yet.