Fix SystemError when convert()'ing an out-of-range int to float by STiFLeR7 · Pull Request #1162 · msgspec/msgspec · GitHub
Skip to content

Fix SystemError when convert()'ing an out-of-range int to float - #1162

Open
STiFLeR7 wants to merge 1 commit into
msgspec:mainfrom
STiFLeR7:fix/convert-float-overflow-systemerror
Open

Fix SystemError when convert()'ing an out-of-range int to float#1162
STiFLeR7 wants to merge 1 commit into
msgspec:mainfrom
STiFLeR7:fix/convert-float-overflow-systemerror

Conversation

@STiFLeR7

Copy link
Copy Markdown

Summary

msgspec.convert(obj, float) converts a Python int to a C double via PyLong_AsDouble without checking for overflow. For an int too large to represent as a finite float (e.g. 10**400), PyLong_AsDouble sets OverflowError internally but still returns -1.0, and that value was passed straight through to ms_decode_float and returned to the interpreter with the exception still set — which surfaces as:

SystemError: <built-in function convert> returned a result with an exception set

This bypasses the documented ValidationError contract entirely, so a caller wrapping the call in except msgspec.ValidationError doesn't catch it.

json.decode already handles the equivalent case correctly, raising ValidationError: Number out of range. This PR applies the same "check PyErr_Occurred() after a lossy C conversion, then report via ms_error_with_path" pattern already used elsewhere in this file (e.g. _constr_as_f64), so convert() reports the overflow the same way — for both bare (convert(big, float)) and nested (convert({"x": big}, dict[str, float])) targets, under both strict=True and strict=False.

Fixes #1122.

Testing

  • Reverted the fix locally and reproduced the exact reported SystemError before reapplying it.
  • Added test_float_from_int_out_of_range to TestFloat in tests/unit/test_convert.py, covering bare and nested targets under both strict modes.
  • Ran the full tests/unit/ suite: 6052 passed, 473 skipped, 0 failures.

msgspec.convert(obj, float) converts a python int to a C double via
PyLong_AsDouble without checking for overflow. For an int too large to
represent as a float, PyLong_AsDouble sets OverflowError but still
returns -1.0, and that value was passed straight through to
ms_decode_float and returned to the interpreter with the exception
still set - which surfaces as SystemError: <built-in function convert>
returned a result with an exception set, bypassing the documented
ValidationError contract entirely.

json.decode already handles the equivalent case correctly, raising
ValidationError: Number out of range. Apply the same
value-error-with-path check convert() already uses elsewhere in this
file (e.g. _constr_as_f64) so the overflow is reported the same way,
for both bare and nested (e.g. dict[str, float]) targets, under both
strict and lax mode.

Fixes msgspec#1122.

@Siyet Siyet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified locally with current main (7c2f473) merged into this branch, Python 3.14.6, -X dev:

  • convert(10**400, float), the nested dict[str, float], tuple[float, ...] and Struct-field cases, strict=False, and the negative value all raise ValidationError: Number out of range with the right path suffix ($[...], $[1], $.x).
  • 2**1024 is rejected, 2**1023 and int(sys.float_info.max) still convert.
  • The OverflowError that PyLong_AsDouble sets does not leak: __context__ and __cause__ are None, sys.exc_info() is clean afterwards.
  • Full unit suite: 6387 passed, 143 skipped.

The check mirrors what _constr_as_f64 already does a few thousand lines up, and the message matches the JSON path. Bug fix with no API change, so I will put it in the merge queue once the CI runs (just approved for the first-time-contributor gate) come back green. The changelog entry gets added in the release-prep batch, same as the other fixes.

@codspeed-hq

codspeed-hq Bot commented Sep 5, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

convert leaks SystemError on an out-of-range intfloat, where json.decode cleanly raises ValidationError

2 participants