Skip to content

Preserve large-integer precision when decoding tool arguments#528

Open
PratikDhanave wants to merge 1 commit into
microsoft:mainfrom
PratikDhanave:fix-jsonformat-int-precision
Open

Preserve large-integer precision when decoding tool arguments#528
PratikDhanave wants to merge 1 commit into
microsoft:mainfrom
PratikDhanave:fix-jsonformat-int-precision

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

Summary

applySchema (agent/format/jsonformat/encoding.go), reached via Format.Unmarshal, decodes incoming JSON arguments into an interface{} with json.Unmarshal, which turns every JSON number into a float64. Integer arguments beyond 2^53 are therefore silently truncated before being re-marshalled and handed to the typed handler:

// handler takes struct{ N int64 `json:"n"` }
tool.Call(ctx, `{"n":9007199254740993}`)   // 2^53 + 1
// handler receives N == 9007199254740992  (off by one) — no error

Every functool call with an int64/uint64 argument is affected. (The output / Normalize path is not — it operates on the already-typed value and doesn't round-trip through this number-losing step.)

Fix

Decode with json.Decoder + UseNumber(), so numbers are preserved as json.Number and round-trip exactly through the re-marshal.

Public API

No exported symbols change.

Tests

Adds TestFormat_Unmarshal_PreservesLargeIntegerPrecision (black-box): a 2^53+1 argument. It comes back as 9007199254740992 before this change and exactly 9007199254740993 after. Full ./agent/format/jsonformat and ./tool/functool suites pass (no regression).

applySchema decoded incoming JSON arguments into an interface{} via
json.Unmarshal, which turns every JSON number into a float64. Integer
arguments beyond 2^53 (int64/uint64) were therefore silently truncated
before being re-marshalled and handed to the typed handler — e.g. a tool
called with {"n":9007199254740993} received 9007199254740992.

Decode with json.Decoder.UseNumber() so numbers are preserved as json.Number
and round-trip exactly. Only the input path (Format.Unmarshal) was affected;
the output/Normalize path already operates on the typed value.

Adds a black-box test asserting a 2^53+1 argument round-trips exactly.
@PratikDhanave
PratikDhanave requested a review from a team as a code owner July 17, 2026 18:48
Copilot AI review requested due to automatic review settings July 17, 2026 18:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes loss of integer precision (> 2^53) when decoding tool-call JSON arguments by switching the schema-application step to decode numbers as json.Number instead of float64, ensuring large int64/uint64 arguments round-trip correctly into typed handlers.

Changes:

  • Update applySchema to decode JSON with json.Decoder + UseNumber() to preserve large integer precision.
  • Add a black-box regression test proving Format.Unmarshal preserves a 2^53+1 integer.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
agent/format/jsonformat/encoding.go Switch argument decoding to UseNumber() in the schema/defaults path to avoid float64 truncation.
agent/format/jsonformat/encoding_test.go Add regression test ensuring Unmarshal preserves large int64 values.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +73 to 80
// Decode with UseNumber so integers beyond 2^53 are not silently
// truncated by being decoded into float64 and re-marshalled.
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()
if err := dec.Decode(&v); err != nil {
return nil, fmt.Errorf("unmarshaling arguments: %w", err)
}
}
Comment on lines 5 to 9
import (
"bytes"
"encoding/json"
"fmt"

Comment on lines +73 to +77
// Decode with UseNumber so integers beyond 2^53 are not silently
// truncated by being decoded into float64 and re-marshalled.
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()
if err := dec.Decode(&v); err != nil {
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.

2 participants