JSON gotchas every developer hits at least once

A short tour of JSON pitfalls — trailing commas, NaN, integer precision, and the difference between JSON and JavaScript.

February 20, 20263 min readby SnapToolsdevjson

JSON looks deceptively simple — and is, until it isn't. Here's a cheat sheet of the corners that catch developers out, in roughly the order you'll hit them.

Trailing commas are not allowed

The single most common JSON error:

{
  "a": 1,
  "b": 2,
}

That trailing comma after 2 is fine in JavaScript, fine in JSONC, fine in TOML. It is not fine in standard JSON. JSON.parse will throw. Most config-file parsers (e.g. Babel, ESLint, TypeScript) actually use JSONC under the hood and tolerate trailing commas, which makes the lesson easier to forget. Run a real JSON validator (like our JSON formatter) before committing.

Single quotes are not allowed

{ 'a': 1 }

Strings (and keys, which are strings) must be in double quotes. Single quotes will throw. JavaScript object literals accept single quotes; JSON does not.

Keys must be strings

{ a: 1 }

That's a JavaScript object literal. Valid JS, invalid JSON. JSON requires {"a": 1}.

NaN and Infinity aren't representable

JSON.stringify({ x: NaN, y: Infinity })
// {"x":null,"y":null}

JSON.stringify converts both to null. JSON.parse will reject the literal text NaN or Infinity. If you need these values, encode them as the string "NaN" or use a sentinel like null and document the meaning.

Integer precision tops out at 2^53

JSON.parse('{"id":9007199254740993}')
// { id: 9007199254740992 }   // ← precision loss!

JavaScript numbers are 64-bit floats. Integers beyond 2^53 (≈ 9 quadrillion) lose precision silently. This bites people storing 64-bit IDs (Twitter snowflakes, BigQuery row IDs) as numbers. Encode them as strings: {"id":"9007199254740993"}. Most modern APIs do this for exactly this reason.

Dates aren't a thing

There's no native date type in JSON. Convention is to use ISO 8601 strings: "2026-01-15T09:30:00Z". When you parse, you get a string back; you have to call new Date(s) yourself. If you stringify a Date object, it'll be serialized as an ISO string by default.

Comments aren't allowed

// this is a comment
{ "a": 1 }

Standard JSON has no comments. JSONC (used by VSCode settings, tsconfig.json) allows // and /* */. JSON5 also allows them. If your file is meant to be parsed by JSON.parse, strip the comments first.

Order of keys is preserved (in practice)

The spec says object members are unordered. In practice, every modern JavaScript engine preserves insertion order, and so does JSON.stringify. Don't rely on order — but you can usually trust it for cosmetic concerns like prettier output. Our formatter has a "Sort keys" option if you want canonical ordering.

Duplicate keys are technically allowed

{ "a": 1, "a": 2 }

The spec says implementations should accept it; behavior is undefined. JavaScript's JSON.parse keeps the last value. Don't write JSON like this — it's a footgun waiting for the wrong parser.

Whitespace is mostly meaningless

Pretty-printing changes nothing semantically. Use JSON.stringify(obj, null, 2) for human readability, JSON.stringify(obj) for shipping. The size difference is real for large payloads but the parse cost is the same.

When in doubt, validate

When something's not working, the fastest path is usually: paste the suspicious payload into a strict validator and let it tell you the line and column where it breaks. We built the formatter precisely for this — it surfaces the exact position of any syntax error rather than the vague "Unexpected token" you get in the console.