Tools › Guides › How to fix a JSON syntax error
How to fix a JSON syntax error
What every JSON error message actually means, where to look, and the six mistakes that cause almost all of them.
Read the position, not the message
Almost every JSON error message names a symptom rather than a cause. The parser tells you where it gave up, which is usually a line or two after the thing that is actually wrong.
Unexpected token } in JSON at position 214 does not mean the closing brace is wrong. It means the parser reached that brace while still expecting something else, most often a value that was never supplied, or a comma that was left behind by a deleted line.
So: go to the position, then look backwards from it. The fault is nearly always on the preceding line.
The six mistakes
A trailing comma. The single most common one, because almost every other language allows it.
`` {"a": 1, "b": 2,} ``
JSON does not. Nor after the last element of an array. If you have just deleted the last property of an object, check the line above it.
Single quotes. JSON strings are double-quoted, always. {'a': 1} is valid JavaScript, valid Python, and not JSON. This is the usual cause when a config file "worked yesterday" and someone edited it in a hurry.
An unquoted key. {a: 1} is a JavaScript object literal. JSON needs {"a": 1}. Keys are strings, with no exceptions.
A comment. JSON has no comments. Not //, not /* */, not #. If you need to annotate a config file, either the format is wrong for the job or you add a "_comment" key and live with it.
A raw newline inside a string. A string cannot contain a literal line break; it has to be \n. This one usually arrives by pasting a multi-line value in from somewhere else.
A missing value. {"a": , "b": 2}: the comma after a is where the parser complains, but the hole is the problem.
Two more that look like corruption
A byte order mark. Some Windows editors write three invisible bytes at the start of a UTF-8 file. The parser sees an unexpected character at position 0 and the file looks perfectly fine in every editor you open it in. If the error is at the very start and you cannot see anything there, this is why.
Smart quotes. Pasting JSON through a word processor, a chat client or a Word document turns " into " and ". They are different characters and the parser will not accept them. The same happens to hyphens becoming en dashes inside numbers.
NaN, Infinity and undefined
None of them are JSON. If you are generating the file from code, a NaN that got into an arithmetic result will serialise to something no parser accepts, JSON.stringify writes null for it in JavaScript, but plenty of other languages emit the literal text NaN, which then fails at the other end.
One thing to watch in your formatter
Most JSON formatters (including, until recently, ours) parse the text into memory and print it back out. That sounds harmless and is not, because most languages have one number type and it is a 64-bit float. Everything about the way a number was written is lost in the round trip, and the printer invents a fresh spelling on the way out:
| You paste | A round-tripping formatter returns |
|---|---|
{"id": 12345678901234567890} | {"id": 12345678901234567000} |
{"version": 1.0} | {"version": 1} |
{"bytes": 1e3} | {"bytes": 1000} |
The first of those is not a formatting difference. It is a different ID, handed back without a word by a tool whose whole job was to add whitespace. Anyone working with snowflake IDs, account numbers or high-precision decimals has been bitten by this.
The fix is for the formatter to re-print from the original text rather than from a parsed value, changing whitespace and nothing else. Ours does, and a test asserts it: strip the whitespace from the output and it is byte-identical to the input.
If you are relying on a formatter you did not write, it is worth pasting {"id": 12345678901234567890} into it once to find out which kind it is.
When the file is genuinely large
Beyond a few megabytes, a browser formatter stops being the right tool, not because it cannot parse it, but because rendering a few hundred thousand lines into a text area is what makes the tab unresponsive. At that size, jq from a terminal is faster and will point at the same line.