JSON has no version number, no comments, and a specification short enough to read over coffee. That austerity is why it won. It is also why a payload that looks perfectly reasonable gets rejected with an error pointing at character 1,847 of a single unbroken line.
The Five Things JSON Does Not Allow
Most parse failures come from writing JavaScript instead of JSON. The two look alike, and JSON is the stricter subset.
Trailing commas are forbidden. `[1, 2, 3,]` is valid JavaScript and invalid JSON. This is the single most common cause of parse errors, because most editors and languages allow them everywhere else.
Comments do not exist. There is no `//` and no `/* */`. Douglas Crockford removed them deliberately, having seen people use comments to smuggle in parsing directives. If you need to annotate, add a real key such as `"_comment"`.
Single quotes are not strings. `{'name': 'value'}` is invalid; JSON requires double quotes on both keys and values.
Unquoted keys are invalid. `{name: "value"}` is a JavaScript object literal, not JSON.
Special numeric values have no representation. `NaN`, `Infinity` and `-Infinity` cannot be expressed, which is why serialising them in JavaScript silently produces `null`. Undefined values disappear from objects entirely.
Reading Parser Error Messages
Error text varies by runtime. The underlying cause rarely does.
| Message | Usual cause | Where to look |
|---|---|---|
| Unexpected token } in JSON | Trailing comma before the brace | The line above the reported position |
| Unexpected token o in JSON at position 1 | An object was passed instead of a string | The call site, not the JSON |
| Unexpected end of JSON input | Truncated response or empty body | Network tab; check the response length |
| Unexpected token < in JSON at position 0 | An HTML error page arrived instead of JSON | The HTTP status; usually a 404 or 500 |
| Bad control character in string | A raw newline or tab inside a string | Escape it as \n or \t |
| Duplicate key | Same key twice in one object | Only some parsers report this; most keep the last |
The <!DOCTYPE error nobody recognises at first
"Unexpected token < in JSON at position 0" almost never means your JSON is broken. It means the server returned HTML, typically an error page or a login redirect, and your code tried to parse it as JSON anyway. Check the status code before parsing the body and this class of bug disappears.
Indentation and Key Order
JSON itself does not care about whitespace, so formatting is purely for humans. Two conventions are worth following.
Two-space indentation is the de facto standard, used by npm, most language formatters and the majority of public APIs. Four spaces is not wrong but wastes horizontal space on deeply nested structures, and nesting is where JSON gets hard to read. Tabs work but render inconsistently across tools.
Key order is not significant to any parser; the specification defines objects as unordered. It is very significant to humans and to version control. Sorting keys alphabetically makes diffs meaningful, because a changed value shows as one changed line rather than a reordered block. For configuration files that live in git, sorting is worth enforcing. For API responses, leave the order the server chose, since it often groups related fields in a way that helps reading.
When to Minify and When Not To
Minified JSON strips every optional space and newline. Whether that is a win depends entirely on where the payload is going.
Minify for network transfer
Whitespace can be 10 to 20 percent of a formatted payload. Over a network, at scale, that is real bandwidth. Any HTTP API should send minified JSON and let the client format it for display.
Do not rely on it after compression
Gzip and Brotli compress repeated whitespace efficiently, so the saving from minification shrinks considerably once compression is enabled. Enable compression first; it does far more than minifying ever will.
Never minify files humans edit
Configuration files, fixtures and anything in version control should stay formatted. A minified file produces a one-line diff for every change, which makes code review effectively impossible.
Format logs, but carefully
Structured logging usually writes one JSON object per line, which is deliberate. Pretty-printing breaks line-based tooling like grep and tail. Keep log JSON minified and format it when you read it.
Format and validate your JSON
Pretty-print at 2 or 4 spaces and see exactly where a parse error is. Nothing is uploaded.
The Number Problem Nobody Warns You About
JSON numbers have no defined precision limit, but the parsers reading them do. JavaScript parses every JSON number into a 64-bit float, which represents integers exactly only up to 2^53 - 1, that is 9,007,199,254,740,991.
An ID larger than that comes back wrong. Not rejected, not flagged: wrong. `9007199254740993` parses as `9007199254740992`, and the code carries on with a value that no longer matches anything in the database. Twitter hit this publicly in 2010 and ended up sending every ID twice, once as a number and once as a string, which is why their API had both `id` and `id_str`.
The rule that avoids this: send large identifiers as strings. Snowflake IDs, database bigints and anything above 2^53 belong in quotes. You lose nothing, because you never do arithmetic on an ID, and you avoid a bug that is almost impossible to spot in testing.
Frequently Asked Questions
Related Tools
Keep Reading
Base64 Is Not Encryption: What It Does and Why It Exists
Three bytes become four characters, which is where the 33 percent size increase and the trailing equals signs come from. And why it hides nothing.
URL Encoding: encodeURI vs encodeURIComponent, and the Plus Sign
The two JavaScript functions are not interchangeable, and picking the wrong one is the most common URL bug there is. Plus where %2520 comes from.
camelCase, snake_case, kebab-case: Which Case Goes Where
The convention each language expects, the one language where capitalisation changes behaviour, and why acronyms break every rule.
SHA-256 Explained: What Hashes Guarantee and What They Do Not
Three properties make a hash cryptographic, and each one supports a different use. Why SHA-256 is the wrong tool for storing passwords.