> ## Content Index
> Fetch the complete content index at: https://debugly.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# The Order ID That Lost Its Last Three Digits in JavaScript
- URL: https://debugly.dev/bigint-order-id-lost-precision/
- Published: 2026-10-09T16:22:00.000Z
- Updated: 2026-10-17T05:55:52.000Z
- Description: The identifier came back from the API as a number, and the number exceeded the safe integer range, so the parsed value rounded, and the lookup found a…
- Author: Rohit Bhadani
- Tags: Bug Hunt, JavaScript, JSON

I started at the incident, because that is where the symptom landed, and walked it backwards link by link until the loaded stopped looking innocent.

Strip the integer precision loss in JSON parsing down and one property makes it silent: the parsed number is a valid number, so nothing throws, and the valid number is the wrong identifier, and the wrong identifier finds a real record.

The case was a browser client talking to an API with snowflake identifiers; the mechanics generalise to any language whose default number type is the double.

## Why the digits were lost

The identifier was the nineteen digits, and the nineteen was the beyond, and the beyond was the safe integer, and the safe integer was the two to the fifty three, and the fifty three was the sixteen digits, and the sixteen was the less than the nineteen, and the less was the rounding, and the rounding was the mantissa, and the mantissa was the double's limit, and the limit was the last digits' loss, and the loss was the different identifier.

The JSON parse was the automatic, and the automatic was the number, and the number was the double, and the double was the rounded, and the rounded was the silent, and the silent was the no error, and the no error was the confidence, and the confidence was the lookup, and the lookup was the found, and the found was the other order, and the other order was the incident.

The rounding is the nearest representable, and the nearest is the close, and the close is the plausible, and the plausible is the found record, and the found record is the confusion, because the wrong identifier is not the absent, it is the present, and the present is the real order, and the real order is the wrong one, and the wrong one is the incident.

## Why the wrong order looked correct

The order was the real, and the real was the customer's, and the customer's was the plausible, and the plausible was the display, and the display was the fields, and the fields were the similar, and the similar was the same customer, and the same customer was the multiple orders, and the multiple was the confusion, because the identifiers differed in the last digits and the display did not show the identifier.

The diagnosis was the comparison, and the comparison was the sent against the received, and the against was the difference, and the difference was the last three digits, and the three was the rounding, and the rounding was the precision, and the precision was the JavaScript's number, and the number was the fix's target, and the target was the string.

This is the same class as [the JSON prototype pollution that came through a parsed object](https://debugly.dev/json-prototype-pollution/), which is why the two fixes look alike.

## What I changed

**Sent the identifier as a string in the API.** The API serialised the identifier as the string, and the string was the exact, and the exact was the no rounding, and the no rounding was the fix, and the fix was the API's change, and the change was the contract, and the contract was the discipline, because the identifier is the label and not the quantity, and the label should be the string.

**Parsed the JSON with a bigint-aware reviver on the client.** The reviver converted the numeric identifiers, and the converted was the exact, and the exact was the fix, and the fix was the parse, and the parse was the interim, and the interim was the bridge until the API changed, and the bridge was the discipline.

**Used BigInt for the arithmetic where needed.** The comparison and the sort used the BigInt, and the BigInt was the exact, and the exact was the fix, and the fix was the type, and the type was the explicit, and the explicit was the discipline, because the implicit number is the rounding.

**Asserted the identifier's round trip in the test.** The test sent the identifier and compared the received, and the compared was the equality, and the equality was the exact, and the exact was the fix, and the fix was the test, and the test was the guard, and the guard was the prevention, because the round trip is the precision's check.

**Documented the identifier's type in the schema.** The schema said the string, and the string was the contract, and the contract was the client's code generation, and the generation was the consistency, and the consistency was the fix, and the fix was the schema, and the schema was the discipline, because the undocumented type is the assumption.

## If it happens again

A JSON number is parsed as a double, so any integer beyond two to the fifty three is rounded silently, and the rounded value is a valid number that may match a different record. Send identifiers as strings, use a bigint-aware parser where you must accept numbers, and assert the round trip in a test.

The client requested one order and displayed another, because the nineteen-digit identifier exceeded JavaScript's safe integer range and the JSON parse rounded the last three digits to the nearest representable double, which happened to be a real order for the same customer. Get that property wrong and every layer above it lies to you politely.

---

Published on [debugly.dev](https://debugly.dev/) by [Rohit Bhadani](https://debugly.dev/about/) | Founder of [Krova Cloud](https://krova.cloud/?ref=debugly.dev)