Skip to content

fix(serde): error instead of returning null for numbers out of range - #86

Merged
dsherret merged 2 commits into
mainfrom
fix_serde_number_out_of_range
Jul 26, 2026
Merged

fix(serde): error instead of returning null for numbers out of range#86
dsherret merged 2 commits into
mainfrom
fix_serde_number_out_of_range

Conversation

@dsherret

Copy link
Copy Markdown
Member

Closes #85

Problem

A JSON number too large for an f64 silently deserialized to null:

let j: serde_json::Value = parse_to_serde_value("{\"amount\": 1e400}", &Default::default()).unwrap();
assert_eq!(j.to_string(), "{\"amount\":null}"); // passed

str::parse::<f64> resolves to f64::INFINITY instead of erroring, and serde_json's Value visitor routes non-finite floats through Number::from_f64, which returns None and produces Value::Null. Deserializing to a plain f64 yielded inf.

Fix

visit_number now returns a ParseError when the number can't be represented, so { "amount": 1e400 } errors with Number is out of range on line 1 column 13. This matches serde_json::from_str, which errors with number out of range for the same input.

While in there, the hexadecimal branch had the same class of bug — it fell back to visiting the raw literal as a string when it didn't fit in an i64. It now:

  • errors for out of range values (ex. 0xFFFFFFFFFFFFFFFFFF), instead of deserializing as "0xFFFFFFFFFFFFFFFFFF"
  • parses via i128 so values that fit in a u64 (ex. 0x8000000000000000) and -0x8000000000000000 (exactly i64::MIN) deserialize as numbers, instead of as strings

The trailing visitor.visit_str(raw) fallback for unparseable decimals is gone since it was unreachable — every number token the scanner produces is valid Rust float syntax, so parse::<f64> only ever fails to give a usable value by overflowing.

Behaviour change

Input that previously deserialized (as null, inf, or a string) now errors, including when the value's field is ignored by the target type — serde_json errors there too.

Note this leaves the non-serde conversions diverging: impl From<ast::Value> for serde_json::Value and CstNumberLit::to_serde_value still turn 1e400 into Value::String("1e400"), and their signatures (From / -> Option<_>) can't surface a parse error. Happy to follow up if you'd like those to return None or be documented.

Tests

Added it_should_error_when_number_is_out_of_f64_range and it_should_error_when_hexadecimal_number_is_out_of_range, covering positive/negative overflow, error positions, the largest finite f64, underflow to ±0.0, out-of-range values in ignored fields, and the hex cases above.

dsherret added 2 commits July 26, 2026 09:56
#85)

A number too large for an `f64` (ex. `1e400`) deserialized to
`serde_json::Value::Null` with no error, because `str::parse::<f64>`
resolves to infinity instead of erroring and `serde_json` turns
non-finite floats into `null`.

Now these error with "Number is out of range", which matches what
`serde_json::from_str` does for the same input.

The hexadecimal branch had the same class of bug, where an out of range
number was visited as a string. It now errors as well and additionally
handles values that fit in a `u64` (ex. `0x8000000000000000`) or that
are exactly `i64::MIN` (`-0x8000000000000000`), which previously
deserialized as strings.
Only falls back to an i128 for the values an i64 can't hold, which are
the ones that fit in a u64 and `i64::MIN`.
@dsherret
dsherret merged commit 5c271cc into main Jul 26, 2026
3 checks passed
@dsherret
dsherret deleted the fix_serde_number_out_of_range branch July 26, 2026 14:10
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.

Number overflowing f64 deserializes as null into serde_json::Value

1 participant