Priority: P1
Describe the issue
Wreq::Response#json silently converts some integral JSON numbers to Float. That loses precision even though Ruby's standard JSON.parse returns an exact arbitrary-precision Integer for the same bytes. The request-side json: converter also rejects integers just above the signed platform range.
Identifiers, timestamps, counters, and pagination tokens are commonly represented as JSON numbers. Silent numeric corruption is particularly hard to diagnose because parsing succeeds and the returned value looks plausible.
Reproduction
Given a response body of:
{"id":9223372036854775808}
wreq 1.2.4 behaves as follows:
require "json"
require "wreq"
response.json["id"]
# => 9.223372036854776e+18 (Float)
JSON.parse(response.bytes)["id"]
# => 9223372036854775808 (Integer)
The request path rejects the same Ruby integer before sending:
client.post(
"http://127.0.0.1:1",
json: {id: 9_223_372_036_854_775_808}
)
# RangeError: bignum too big to convert into `long long`
Required behavior
- Integral JSON tokens must deserialize to exact Ruby
Integer values, not Float approximations.
- Fractional or exponent-form JSON numbers continue to deserialize according to documented Ruby JSON behavior.
- Request
json: serialization should accept the integer range accepted by Ruby's JSON.generate. If the native serializer intentionally supports a smaller range, it must document that limit and raise before any network I/O; silent coercion is never acceptable.
- Do not turn large numbers into strings unless the caller supplied strings.
- Preserve the existing return shapes for objects, arrays, strings, booleans, and
null.
Delegating the convenience helper to Ruby's JSON implementation is one possible solution; using an integer-preserving native representation is another. The issue is about compatible observable behavior, not a required implementation.
Acceptance criteria
Response#json matches JSON.parse(response.bytes) for integral number values.
- Request and response tests cover
2**53 - 1, 2**53, 2**63 - 1, 2**63, 2**64 - 1, negative boundaries, and an integer beyond 64 bits.
- Nested arrays and objects preserve the same values.
- Unsupported input raises a documented Ruby exception before DNS or socket I/O.
- Invalid JSON still raises the documented decoding/parser exception.
Ruby ecosystem precedent
Ruby's standard JSON.parse maps integral JSON numbers to Integer and fractional numbers to Float. Ruby integers have arbitrary precision, so callers do not normally expect a native binding to narrow a syntactically integral value silently.
wreq-python's JSON converter also uses a platform-sized signed integer representation. That demonstrates a recurring binding-conversion limitation, not a cross-language API contract Ruby should copy.
Relevant wreq-ruby source
Priority: P1
Describe the issue
Wreq::Response#jsonsilently converts some integral JSON numbers toFloat. That loses precision even though Ruby's standardJSON.parsereturns an exact arbitrary-precisionIntegerfor the same bytes. The request-sidejson:converter also rejects integers just above the signed platform range.Identifiers, timestamps, counters, and pagination tokens are commonly represented as JSON numbers. Silent numeric corruption is particularly hard to diagnose because parsing succeeds and the returned value looks plausible.
Reproduction
Given a response body of:
{"id":9223372036854775808}wreq 1.2.4 behaves as follows:
The request path rejects the same Ruby integer before sending:
Required behavior
Integervalues, notFloatapproximations.json:serialization should accept the integer range accepted by Ruby'sJSON.generate. If the native serializer intentionally supports a smaller range, it must document that limit and raise before any network I/O; silent coercion is never acceptable.null.Delegating the convenience helper to Ruby's JSON implementation is one possible solution; using an integer-preserving native representation is another. The issue is about compatible observable behavior, not a required implementation.
Acceptance criteria
Response#jsonmatchesJSON.parse(response.bytes)for integral number values.2**53 - 1,2**53,2**63 - 1,2**63,2**64 - 1, negative boundaries, and an integer beyond 64 bits.Ruby ecosystem precedent
Ruby's standard
JSON.parsemaps integral JSON numbers toIntegerand fractional numbers toFloat. Ruby integers have arbitrary precision, so callers do not normally expect a native binding to narrow a syntactically integral value silently.wreq-python's JSON converter also uses a platform-sized signed integer representation. That demonstrates a recurring binding-conversion limitation, not a cross-language API contract Ruby should copy.Relevant wreq-ruby source
src/client/body/json.rssrc/client/resp.rs