JSON has one of the smallest grammars of any data format, which is exactly why it fails in ways that look absurd. Almost every parse error is one of five things.
The five reasons JSON fails
1. A trailing comma
{ "name": "RankCert", "verified": true, }
Legal in JavaScript object literals, legal in JSON5, legal in most config formats. Not legal in JSON. This is by far the most common cause, because every developer's fingers are trained by JavaScript.
2. Single quotes
{ 'name': 'RankCert' }
JSON strings are double-quoted. Always. Both keys and values. There is no exception.
3. Unquoted keys
{ name: "RankCert" }
Valid JavaScript, invalid JSON. Keys are strings, so they take quotes.
4. A literal newline inside a string
{ "note": "line one
line two" }
Control characters below U+0020 must be escaped. Write \n, not an actual line break.
5. Comments
{
// the product name
"name": "RankCert"
}
JSON has no comment syntax. Neither // nor /* */. If you need commented config, you need JSONC, YAML or TOML - not JSON.
Reading the error message
JSON.parse reports a character offset, not a line number, which is useless on a 4,000-character payload. Convert it:
try {
JSON.parse(source);
} catch (error) {
const position = Number(String(error.message).match(/position (\d+)/)?.[1]);
const before = source.slice(0, position);
const line = before.split("\n").length;
const column = position - before.lastIndexOf("\n");
console.error(`${error.message} - line ${line}, column ${column}`);
}
Note that the reported position is usually where the parser gave up, which is one token after the actual mistake. A trailing comma on line 12 typically reports at the closing brace on line 13. Look at the line before the one you are given.
Things JSON allows that surprise people
Duplicate keys are legal. The spec does not forbid them; it leaves the behaviour to the implementation. JSON.parse keeps the last one silently. If your API accepts JSON from clients, a duplicate key is a real attack surface - two parsers in your stack may disagree about which value won.
Top-level scalars are valid. 42, "hello" and null are each a complete, valid JSON document. Not every parser written before 2014 agrees.
Key order is preserved in practice. The spec says objects are unordered, but every mainstream parser preserves insertion order. Do not rely on it across languages.
Numbers have no defined precision. JSON.parse produces IEEE 754 doubles, so any integer above 2^53 loses precision silently. A 64-bit database ID sent as a JSON number will come back wrong. Send large IDs as strings.
Things JSON does not allow that you might expect
NaN,Infinity,-Infinity- not valid.JSON.stringifyconverts them tonullwithout warning.undefined- dropped entirely from objects, converted tonullinside arrays.- Dates - there is no date type.
JSON.stringify(new Date())produces an ISO string, and parsing gives you a string back, not a Date. BigInt- throws on stringify. Convert to a string first.- Trailing decimal points -
1.is invalid; write1.0. - Leading zeros -
007is invalid.
Minifying, and when it matters
Whitespace typically accounts for 15–30% of a formatted JSON payload. For anything you transmit, minify it and let gzip do the rest - though note that gzip already handles repeated whitespace well, so the real-world saving after compression is smaller than the byte count suggests.
Where minifying genuinely matters is anywhere the payload is stored uncompressed at scale: a jsonb column, a log line, a cache entry.
For anything a human reads - config files, fixtures, API examples - format with two spaces and never think about it again.
Validating structure, not just syntax
A document can be perfectly valid JSON and still be wrong for your use. Syntax validation says the braces match; it says nothing about whether email is present or age is a number.
For that you want a schema. JSON Schema is the portable option; in TypeScript, a runtime validator like Zod gives you both the check and the inferred type from one declaration. Validate at every boundary where data enters your system, because a parser will happily hand you {} when you expected a user.
Launch it where the numbers are checked
RankCert ranks products on domain control we verify ourselves. Listing is free and the link stays dofollow whether or not you display the badge.
