JavaScript was right about the number
Rust sent a count across JSON as an i64. JavaScript turned it into a BigInt. Then one innocent addition crashed the dashboard.
The JamWidgets dashboard crashed while adding two view counts.
Not dividing. Not doing statistics. Adding.
views: e.views + t.viewsLast7Days
One value was a JavaScript number. The other was a BigInt. JavaScript refuses to mix them, so the entire overview died with:
TypeError: Cannot mix BigInt and other types
JavaScript was right.
The API returned database counts as Rust i64. That is the natural type on the server and a bad promise to make through JSON. JavaScript numbers cannot represent every 64-bit integer exactly.
We had already learned this for IDs and serialized them as strings. Counts looked less dangerous because nobody expects a blog to receive nine quadrillion views. Then generated types and conversion code produced a mixture of numbers and BigInts, and ordinary arithmetic found the boundary for us.
The fix was to make the wire format honest. Counts leave Rust as decimal strings. The frontend converts them once, at the query boundary, into BigInt values. Components no longer guess what came over the network.
Could we have cast everything to Number? Yes. These particular counts are only displayed and are nowhere near the unsafe range. That would fix today’s crash.
It would also leave the API contract saying “64-bit integer” while the client quietly says “close enough.” I dislike contracts that become less true as the product succeeds.
This was not really a BigInt bug. It was a boundary bug. Rust, JSON, generated TypeScript, and the UI each had a reasonable local idea of the value. Nobody owned the conversion between them.
Now one place does.
The dashboard adds the numbers. JavaScript remains pedantic. Good.
More where this came from
Subscribe for new posts. Low volume.