Most JSON validation problems run one way: payloads that look fine and fail to parse. Our guide on AI-generated JSON covers that catalog. This guide is the mirror image—documents that look broken, or at least suspicious, yet JSON.parse accepts them without error. These cases matter because hand-written validators and regex guards reject them before the parser ever runs, and because some of them conceal silent data changes.
1. Top-level scalar values
A JSON document does not have to be an object or an array. Since RFC 8259 (2017), a JSON text is any JSON value:
JSON.parse('123'); // 123
JSON.parse('"hello"'); // hello
JSON.parse('true'); // true
JSON.parse('null'); // null The pre-2017 RFC 4627 restricted JSON text to objects and arrays, and that belief survives in a lot of code. API clients that assert typeof value === 'object', and validators that check for a wrapping or [ ], reject legitimate scalar responses—a health endpoint that returns "ok", a counter that returns 42.
2. Scientific notation
The JSON number grammar allows an exponent part, and most people underestimate how much it changes what “a number” means:
JSON.parse('1e3'); // 1000
JSON.parse('1.5e-7'); // 1.5e-7
JSON.parse('-0'); // -0 The exponent may be negative, the mantissa may be a decimal fraction, and the sign may be present or absent. One case that surprises even careful readers: -0 is a distinct value—Object.is(-0, JSON.parse('-0')) is true—and JSON.stringify(-0) later emits "0". The value survives parsing, but not every representation remembers it.
3. A number so large it becomes Infinity
JSON.parse('1e999');
// Infinity This is the exponent-side twin of our large-integer guide: the text is perfectly valid JSON, and the parse succeeds. The value 1e999 simply exceeds Number.MAX_VALUE, so JavaScript must produce something—and chooses Infinity silently. No exception, no warning.
Infinity—and JSON.stringify(Infinity) emits null, corrupting the payload on the way out.A lossless parser keeps the token 1e999 and lets you decide what to do with it, instead of committing to Infinity at parse time.
4. Unicode escapes and escaped characters
String grammar is the one place JSON is surprisingly permissive. Any \uXXXX with four hex digits is legal—there is no check that the code point is valid, and no requirement that a surrogate pair be paired:
JSON.parse('"\u0041"'); // "A"
JSON.parse('"\/"'); // "/"
JSON.parse('"\uD800"'); // lone surrogate, parses without error So "A\u0041lice" and "Alice" are the same string—the escaped form and the direct form are equivalent—and a lone \uD800 is valid JSON text that represents an unpaired surrogate in the decoded string. Validators that whitelist characters, and tools that assume every escape is \n or \t, misjudge real payloads.
5. Duplicate keys
JSON.parse('{"a": 1, "a": 2}');
// { a: 2 } The grammar permits repeated names. RFC 8259 says the names “SHOULD be unique”—a recommendation, not a requirement. JavaScript keeps the last occurrence, as do most other parsers, but the ecosystem is not unanimous: some libraries keep the first, and some throw. The same document can therefore behave differently depending on which parser touches it, which makes duplicate keys a quiet interoperability hazard in generated configs.
If you control the producer, treat uniqueness as a real requirement. If you only consume the payload, know that the value you read depends on the parser’s duplicate policy, not on the document alone.
The five cases at a glance
| Case | Example | JSON.parse result | Why it is legal |
|---|---|---|---|
| Top-level scalar | 123 | 123 | RFC 8259 allows any JSON value as a document |
| Scientific notation | 1e3 | 1000 | Exponent part is part of the number grammar |
| Overflowing exponent | 1e999 | Infinity (silent) | Valid syntax; the value exceeds Number range |
| Unicode escapes | "A" | "A" | Any four hex digits are legal escapes |
| Duplicate keys | {"a": 1, "a": 2} | {"a": 2} | Grammar allows repeats; uniqueness is only a SHOULD |
The mirror list: looks valid, is not
For balance, the inverse cases—leading zeros (01), bare NaN, trailing commas, single quotes, comments—are all invalid. JSON.parse('01') throws “Unexpected number in JSON at position 1”, and JSON.parse('NaN') throws "NaN" is not valid JSON. These look harmless and fail hard, which is exactly the catalog our guide on repairing AI-generated JSON walks through with reproduced errors.
How JSONDock treats these cases
JSONDock parses with a lossless representation, so the five cases above stay true to their source text: 1e999 is shown as 1e999 and flagged as an unsafe number instead of silently becoming Infinity; -0 stays -0; duplicate keys resolve last-wins, explicitly. And for the mirror list, the Repair tab normalizes the syntax before parsing. The inspection surface should not decide the value’s fate for you.