What Counts as Valid JSON

The grammar fits on a single page and the whole format has six types. That simplicity is why JSON won, and it is also why the questions it leaves open have been answered differently by everyone who has implemented it.
If your document parses in one language and fails, or worse, silently changes in another, the cause is almost always one of the ambiguities below rather than a bug.
Two documents claim to define it
Douglas Crockford specified the format around 2001 and published the grammar at json.org. Formal standardisation came much later, and in duplicate.
ECMA-404 appeared in October 2013 and was revised in December 2017. It is remarkably short and does one thing: it states the syntax. It deliberately imposes no requirements on what a parser must accept or how it should behave on anything unusual.
RFC 8259, also from December 2017, is the current IETF specification and carries the designation STD 90. It describes the identical grammar, then adds what ECMA-404 leaves out — the application/json media type, a requirement that text exchanged between systems be encoded in UTF-8, and several paragraphs of interoperability guidance.
Neither supersedes the other. A document can satisfy the grammar both describe and still be something RFC 8259 warns you about, and that gap is where the trouble lives.
Two earlier revisions matter historically. RFC 4627, from 2006, required the top level of a JSON text to be an object or an array. RFC 7159, in 2014, relaxed that so any value could stand alone. So 42 and "hello" are complete JSON documents under the current standard and were not under the original one — which is why an old parser somewhere in your stack may still reject them.
Where implementations part company
Duplicate names
RFC 8259 says names within an object SHOULD be unique and observes that behaviour with duplicates is unpredictable. It does not forbid them. In practice JavaScript keeps the last occurrence, some Python configurations keep the last, strict parsers raise, and a few collect all of them into a list. Nothing in the format tells any of them they are wrong.
Number range
The grammar allows arbitrarily long numbers with no stated limit. RFC 8259 notes that good interoperability is achieved when implementations stay within the range of an IEEE 754 double, which caps exact integers at 9007199254740991. Beyond that, a value that was valid on the way in is a different value on the way out, and no error is raised at any point.
Lone surrogates
A string may contain \uD800 with no matching low surrogate. The grammar permits it, JavaScript's parser accepts it, and it does not correspond to any Unicode character. Anything that re-encodes to UTF-8 downstream is entitled to reject or replace it.
Nesting depth
Neither standard sets a limit, so every parser sets its own or discovers it as a stack overflow.
Nicolas Seriot documented this systematically in 2016 in a piece titled Parsing JSON is a Minefield, alongside a public test corpus of ambiguous and malformed documents. He ran it against roughly thirty parsers. Not one pair agreed on every case, and several crashed outright.
The missing comment syntax
Comments are absent by choice, not oversight. Crockford has said he removed them because he saw people using comments to carry parsing directives, which would have fragmented the format into dialects that only certain readers could understand.
The consequence is that every configuration file wanting annotations has needed a superset. JSON5 and JSONC both add comments and trailing commas; tsconfig.json and VS Code settings use the latter. These are useful formats. They are not JSON, and a compliant parser is right to reject them.
Our JSON formatter will not strip comments before parsing for that reason. Silently accepting a document that is not JSON teaches you something false about what your own pipeline will accept.
When the container is the wrong one
JSON has no framing. A parser reads a complete document, which means a 4 GB export must be fully in memory before you can look at the first record.
NDJSON, one compact JSON value per line, fixes this without inventing anything — each line parses independently, so you can stream, append, and split the file with standard tools. It is the right shape for logs, exports and event streams.
For genuine analytical volume, a columnar binary format such as Parquet or Arrow will outperform any text encoding by an order of magnitude on both size and scan speed. JSON's advantage was never efficiency. It was that you can open it in any editor and read it, which is precisely why formatting and diffing it remains worth doing at all — JSON Compare exists for the second half of that.