DEV Community

Rasika Dangamuwa
Rasika Dangamuwa

Posted on

JSON Edge Cases Your Parser Silently Ignores (And How to Fix Them)

JSON feels deceptively simple. We use JSON.parse() and JSON.stringify() every day without a second thought. Because the syntax looks identical to JavaScript object literals, developers often assume JSON parsing behaves predictably across environments.

However, the JSON specification (RFC 8259) and language-specific implementations don't always align. Subtle edge cases can cause silent data corruption, lost precision, or unexpected production failures—often without throwing a single error.

Here are three JSON edge cases every developer should watch out for and how to handle them cleanly.


1. The 64-Bit Integer Precision Loss

Standard JavaScript numbers are IEEE 754 double-precision floats. The maximum safe integer in JavaScript is Number.MAX_SAFE_INTEGER (9,007,199,254,740,991 or 2^53 - 1).

Database primary keys, Twitter status IDs, and Snowflake IDs frequently exceed this limit. Consider what happens when your backend sends a 64-bit integer ID in a JSON payload:

{
  "user_id": 112233445566778899
}
Enter fullscreen mode Exit fullscreen mode

When you parse this with native JSON.parse() in JavaScript:

const payload = '{"user_id": 112233445566778899}';
const data = JSON.parse(payload);
console.log(data.user_id); // Output: 112233445566778900
Enter fullscreen mode Exit fullscreen mode

Notice what happened? The parser silently rounded 112233445566778899 up to 112233445566778900. The operation succeeded with zero errors, but your frontend is now querying the database with an incorrect ID.

The Fix:

  • Backends should serialize 64-bit integers as strings ("user_id": "112233445566778899").
  • On the client side, use custom parser libraries like json-bigint when consuming third-party APIs that return unquoted 64-bit numbers.

2. Duplicate Key Collision Vulnerabilities

What happens when an incoming JSON object contains duplicate keys?

{
  "role": "guest",
  "role": "admin"
}
Enter fullscreen mode Exit fullscreen mode

RFC 8259 Section 4 states that key names should be unique, but it does not strictly forbid duplicates. As a result, different JSON parsers handle this inconsistently:

  • V8 / Node.js / Browser JS: Overwrites previous values and retains "admin".
  • Python (json module): Keeps the last key by default ("admin").
  • Go (encoding/json): Overwrites and keeps the last key.
  • Some API Gateways / WAFs: May parse only the first key ("guest").

This inconsistency creates security risks. If your API gateway inspects the payload and sees "role": "guest", but passes the raw JSON to a backend service that evaluates the second key as "role": "admin", an attacker can bypass access controls.

The Fix:
Enforce strict schema validation (using tools like Zod or Ajv) to reject payloads with non-unique keys before processing them in business logic.


3. Unpaired Unicode Surrogates

JSON strings must contain valid UTF-8 characters. In JavaScript, strings are UTF-16 code units. If a string contains an unpaired surrogate (such as "?" without its accompanying low surrogate), JavaScript will happily stringify it:

const invalid = JSON.stringify({ text: "?" });
// Result: '{"text":"?"}'
Enter fullscreen mode Exit fullscreen mode

While JS engines tolerate raw surrogate pairs, sending this string to a strict UTF-8 parser in Rust or Go will cause the request to fail with invalid encoding errors.


Debugging JSON Payloads Client-Side

When troubleshooting malformed JSON responses or checking nested API structures, pasting production payloads into server-side web tools can accidentally expose sensitive tokens or private user data.

Using a browser-based, client-side tool like the Nutilz JSON Formatter ensures your data is parsed locally in your browser memory without hitting external servers. It gives you instant syntax error highlighting and formatted tree views while keeping your payload private.


Best Practices for Defending Against JSON Glitches

  1. Treat 64-bit numbers as strings: Never pass raw 64-bit integers across JSON boundaries.
  2. Validate schemas early: Use a schema validator to reject malformed types and duplicate keys at the network edge.
  3. Use reviver functions: Leverage the optional second argument of JSON.parse(text, reviver) to sanitize types during parsing.
const safeData = JSON.parse(jsonString, (key, value) => {
  if (typeof value === 'number' && value > Number.MAX_SAFE_INTEGER) {
    return BigInt(value);
  }
  return value;
});
Enter fullscreen mode Exit fullscreen mode

Conclusion

JSON is simple on the surface, but edge cases around number precision, duplicate keys, and Unicode encoding can lead to subtle production bugs. Understanding these engine behaviors helps you design more resilient APIs and client applications. For quick, private payload verification during development, you can use the client-side JSON Formatter on nutilz.com to validate and inspect your JSON without leaving your browser.

Top comments (1)

Collapse
 
marcusykim profile image
Marcus Kim

The 112233445566778899 example is a particularly nasty failure because JSON.parse() succeeds while the identifier becomes 112233445566778900; by then, a reviver cannot recover the original value. Duplicate keys create a different boundary problem: validating the already-parsed object with Zod or Ajv will not reveal that two role fields were present, so strict duplicate detection has to happen during raw-token parsing or at the gateway. I'd treat JSON boundaries as contracts with explicit numeric and encoding rules, plus adversarial fixtures for parser differentials, rather than assuming every compliant-looking payload is.