How to fix a JSON syntax error
What the message means in each language, the eight mistakes behind nearly all of them, and how to get from a character offset to the thing you actually typed wrong.
Free toolJSON Formatter & ValidatorPretty-print, minify, validate and explore a JSON payload as a collapsible tree. Errors are pinned to a line and a column.Open the formatterThe short answer
Read the position in the error, then look at the character just before it. A parser reports where it gave up, not where the mistake is, and the mistake is almost always one token earlier. A missing comma is reported at the start of the next property, and an unclosed string is reported at the end of the file.
Eight mistakes account for nearly every JSON error anyone hits: a trailing comma, a missing comma, single quotes, unquoted keys, comments, curly quotes from a word processor, an unescaped backslash or quote inside a string, and a capitalised True or None pasted out of Python.
What your language is telling you
The messages differ enormously in quality. Python names the problem and gives a line and a column; JavaScript gives a character offset and, in older browsers, a message that describes the symptom rather than the cause.
| Message | Where from | What it usually is |
|---|---|---|
| Unexpected token } in JSON at position 42 | JavaScript | A trailing comma before that brace. |
| Expected double-quoted property name in JSON | JavaScript, V8 since 2023 | An unquoted key, or a trailing comma. |
| Unexpected token < in JSON at position 0 | JavaScript | Not JSON at all. An HTML error page. |
| Unexpected end of JSON input | JavaScript | Empty body, or a truncated download. |
| Expecting ',' delimiter: line 3 column 5 | Python | A missing comma between two properties. |
| Expecting property name enclosed in double quotes | Python | An unquoted or single-quoted key, or a trailing comma. |
| Extra data: line 2 column 1 | Python | Two documents in one file. Usually NDJSON. |
| Syntax error | PHP | All of the above. PHP does not say which. |
| Unexpected character ('}' (code 125)) | Java, Jackson | A trailing comma, with the position given. |
PHP is the worst of these by some distance. json_decode returns null and sets an error you have to ask for, so call json_last_error_msg(), or pass JSON_THROW_ON_ERROR and let it throw.
The eight mistakes
1. A trailing comma
Valid in JavaScript, invalid in JSON, and identical on screen. It is reported at the closing brace or bracket rather than at the comma itself.
{ "a": 1, "b": 2, } // the comma after 2 is the error2. A missing comma
Reported at the start of the next property, which is why the line number in the error often looks one too high. Check the end of the line above the one named.
3. Single quotes
JSON has exactly one string delimiter and it is the double quote. Single quotes are a JavaScript and Python habit, and no JSON parser accepts them.
4. Unquoted property names
{ name: "Ada" } is a JavaScript object literal, not JSON. Every key needs its own double quotes.
5. Comments
Neither style is allowed. If the file is a VS Code settings file, a tsconfig or an ESLint config, it is JSONC rather than JSON, and any general parser will reject it.
6. Curly quotes
The signature of JSON that has been through Word, Google Docs, Slack or Notes. Autocorrect replaces the straight quote with a typographic one, which is a different character, and the error reads as an unexpected token where a string should be.
7. An unescaped backslash or quote inside a string
Windows paths are the usual source. "C:\Users\ada" is invalid, because \U is not an escape. Write "C:\\Users\\ada". The same applies to a quote inside a quoted string, which has to be written as \".
8. Python and PHP literals
True, False and None are Python. JSON writes true, false and null, all lower case. This is what you get from pasting the output of a print() rather than of json.dumps(). NaN and Infinity have no JSON representation at all.
Turning a character offset into a place in the file
JavaScript reports a position, not a line, which is useless on a minified payload. Both of these turn one into the other.
// Node: print the 80 characters around position 4812
const text = require('fs').readFileSync('data.json', 'utf8');
console.log(text.slice(4762, 4862));# Python: the message already carries line and column
python -m json.tool data.jsonOr paste the file into the JSON formatter, which reports the line and the column, shows the characters either side of the fault with a mark under it, and jumps the cursor there.
Checking JSON from the command line
Four one-liners, in rough order of how likely the tool is to already be installed.
python -m json.tool data.json # validates and pretty-printsjq . data.json # same, with better errorsnode -e "JSON.parse(require('fs').readFileSync(0,'utf8'))" < data.jsonnpx jsonlint -q data.json # line and column, no installIn VS Code, set the language mode to JSON and the problems panel lists every error with a line number, then Format Document tidies it. Set the mode to "JSON with Comments" for a tsconfig or a settings file, or every comment will be underlined as an error.
When the JSON is not the problem
- You were sent HTML. An error page, a login redirect or a captive portal. Print the first 200 characters of the body before parsing it.
- The response was truncated. A timeout or a dropped connection leaves valid JSON that simply stops. The error is at the very end of the file.
- There is a byte order mark. Saving as UTF-8 in Notepad or Excel puts three invisible bytes at the start. Some parsers cope and some report an unexpected character at position 0. Save as "UTF-8 without BOM".
- It is double-encoded. If the whole document is one string full of
\", it has been serialised twice. Parse it twice, and fix the producer. - It is NDJSON. One object per line, no commas and no wrapping array. Read it line by line.
Not hitting it again
Hand-written JSON is where nearly all of this comes from. Build the value in your language and let the serialiser write it: json.dumps, JSON.stringify or json_encode. None of them can produce a trailing comma or an unquoted key.
For configuration a human has to edit, use a format that expects a human: YAML, TOML, or JSONC if the tool reading it supports comments. And send large integers such as IDs as strings, because JSON has no size limit on a number and the language reading it does.