Guides

Why is my JSON invalid?

· 7 min read

A JSON parser tells you where it gave up, not where you went wrong. Those are usually different places, which is why the reported line number so often looks innocent. Almost every invalid JSON document fails for one of eight reasons.

1. A trailing comma

The single most common cause. JavaScript allows a comma after the last item in an array or object. JSON does not. If you copied an object out of source code, you probably brought a trailing comma with it.

The error usually points at the closing bracket, because that is where the parser realised something was missing, not at the comma that caused it. Look at the character just before the bracket.

2. Single quotes

JSON strings use double quotes, always. Single quotes are valid in JavaScript and Python but not in JSON. This one tends to come from pasting a Python dict, which also brings the next two problems with it.

3. Python values: True, False, None

JSON spells them true, false and null, all lowercase. A capitalised True or a None is a Python repr, not JSON. If you see these, you are looking at printed Python rather than serialised JSON, and the fix is to dump it properly with json.dumps rather than to patch the text.

4. Unquoted keys

Every key in a JSON object is a quoted string. Bare keys are legal JavaScript and a common source of confusion, because a JavaScript object literal and a JSON document look nearly identical until a parser disagrees.

5. NaN and Infinity

JSON has no way to write these. A serialiser that emits NaN or Infinity is producing something JSON parsers will reject, which is a real problem when it comes from a numerical pipeline. The usual fixes are to emit null instead, or to send the value as a string and handle it on the other side.

6. Comments

JSON has no comments. Not double slashes, not hash marks, not block comments. Config files that appear to be JSON and contain comments are usually JSON5 or JSONC, which are different formats with their own parsers. A standard parser will stop at the first slash.

7. An unescaped character inside a string

A raw newline, tab or double quote inside a string must be escaped. This is the one that most often comes from string concatenation: a value containing a quote mark gets dropped in without escaping and ends the string early, after which everything the parser reads is nonsense.

If the reported error is far past where you expected, suspect an unterminated string. The parser kept reading structure as text until it hit something impossible, so the error appears much later than the cause.

8. It is not JSON at all

An HTML error page, a truncated response, or a stack trace where you expected a payload. If the first character is a less-than sign, you have HTML. If the document simply stops mid-value, the response was truncated, and the bug is in the transfer rather than in the JSON.

Reading the error position

What the error saysWhat to look at
Unexpected token at a closing bracketThe comma or value immediately before it
Unexpected end of inputAn unclosed bracket, brace or string
Unexpected token at the very startNot JSON: check for HTML or a BOM
Error far later than expectedAn unterminated string earlier in the document

A validator that shows you the parse position next to the text is faster than reading a line number in a terminal, because the two things you need to compare end up side by side. The validator here runs in the browser, so a payload with credentials in it does not leave the tab.

Frequently asked questions

Why does the error point at the wrong line?

A parser reports where it could no longer continue, which is often after the real mistake. A trailing comma is reported at the following bracket, and an unterminated string is reported wherever the parser finally hit something impossible.

Can JSON have comments?

No. Formats that allow them, such as JSON5 and JSONC, are separate formats with their own parsers. A standard JSON parser rejects the first comment character.

How do I fix a file full of Python True and None?

Do not patch the text. That output is a Python repr rather than JSON, so re-serialise the object with json.dumps, which writes true, false and null correctly.