Interesting Bug finding related to JavaScript

Vinay Mahamuni
3 min readJun 10, 2020

Long Story Short: JavaScript number data type can hold the number only till (2⁵³-1) without loosing precision. If you pass bigger number to JS variable, things will behave like a dark magic.

Big Story

This is about one of the project I was working on. It had list of sensitive data which need to be send to UI and get the selected data back.

The data was list of account ids and user needed to select one of them for further action. As it was a sensitive, we masked the account id with ‘X’ and mapped with extra field uniqueId to uniquely identify the account id. It looks something like below

[
{
accId: "XXXXXXXXXX123",
uniqueId: 9062834707864722
},
{
accId: "XXXXXXXXXX345",
uniqueId: 9061627692585335
},
{
accId: "XXXXXXXXXX789",
uniqueId: 9034549534024327
}
]

The user would see list of accounts in the drop-down. Once the user selects the account, UI would send uniqueId to backend. Backend would verify if that uniqueId matches with any account’s uniqueId, if not, it would throw an exception.

Things worked fine when this feature was developed. Months passed, but we never got any exception, No tests failed. Now after 4 months, when we were not actively working on that service, suddenly, the functional tests started failing. We had no clue why it started failing as we hadn’t changed the code for last 4–5 days.

So, we started debugging it!!!

From above example, when user selected the account ending with 345, UI was sending back uniqueId: 9061627692585336 instead of 9061627692585335.Thus it would not match with the database. This was confusing because It was incrementing by 1. We tried with the account ending with 123, UI sent back 9062834707864722 which matched with database. So why this was acting weird only with some account id’s? We tried to replicate the bug on local machine but feature was working fine on the local machine. (Works on my machine 😛)

Little background on how we were creating uniqueId: We were using java’s System.nanoTime() function combined with hashcode of account id string which would always provide unique number at any given point in time for every accountId.

Thus, everyone thought problem is in the implementation we were using to generate uniqueId. But soon we realized that it is acting weird only with odd uniqueId numbers but worked properly with even uniqueId numbers. This gave us the direction to work.

Now we tried storing the number in JS variable and logging it back. We figured out the issue. The culprit was Number data type of JS. It can store values with the precision only upto (2⁵³-1) i.e 9007199254740991. Once you cross this limit, you will loose the precision.

So if you try running console.log(9007199254740992 === 9007199254740993). it will print true. isn’t this language WEIRD!!

Question arises why the tests didn't failed earlier???

Because, System.nanoTime() returns current value of the running Java Virtual Machine’s high-resolution time source, in nanoseconds. Thus, longer the JVM is running, longer the time will elapse, resulting bigger nano second number. But see, Java’s Long can hold number as big as (2⁶³-1) so even if your JVM is running for 200 years, the variable won’t give “value is out of range” error.

What I Learned in the debugging?

  1. Always know what are the limitations of data types you are using in certain programming language
  2. What is System.nanoTime()
  3. Always mind how you are communicating with different languages and try to use compatible data types for communication.

PS: Please help me with the better title for the story.

--

--