All posts

How to fix common JSON syntax errors

Broken and fixed examples for the JSON errors you hit most: trailing commas, single quotes, comments, NaN and BOMs, plus what each JSON.parse message means.

By Last reviewed 7 min readJSONDebugging

JSON's grammar fits on a few pages of RFC 8259, and it forgives nothing: no comments, no trailing commas, no single quotes, no NaN. Most parse errors come from JSON typed by hand, JSON assembled by gluing strings together, or output that was never JSON at all: a Python print() of a dict, a JavaScript object literal, an HTML error page.

The fastest loop is to paste the text into the JSON validator and jump to the exact line and column. It parses with your browser's own JSON.parse, marks the failing line in the gutter and puts a caret under the character where parsing stopped. Nothing you paste leaves your machine.

Read the error before you edit

The position in a parse error is where the parser gave up, which is often one token after the real mistake. A missing comma shows up at the next key; a missing ] at whatever arrived instead. Read the message, then look just before the caret.

Chrome, Edge and Node.js all use the V8 engine and share its wording. Current versions say Expected double-quoted property name in JSON at position 46 (line 4 column 1); older versions said Unexpected token } in JSON at position 46 for nearly everything. Firefox reports the same problem as JSON.parse: expected double-quoted property name at line 4 column 1 of the JSON data. The common V8 messages map to causes like this:

Message (V8)Usual cause
Expected double-quoted property nameA trailing comma, or an unquoted key or comment after a comma
Expected property name or '}'First key unquoted or single-quoted, or a comment after {
Expected ',' or '}' after property valueMissing comma or } after a value, or a trailing comment
Expected ',' or ']' after array elementMissing comma between items, or a missing ]
Bad control character in string literalRaw newline or tab inside a string
Bad escaped characterInvalid backslash escape, often a Windows path
Unterminated stringMissing closing quote, or truncated input
Unexpected non-whitespace character after JSONExtra closing brace, or a second document
Unexpected end of JSON inputEmpty or truncated input
Unexpected token 'N', "{"ratio": NaN}" is not valid JSONNaN, undefined, a single-quoted value, a trailing comma in an array, a BOM, or the < of an HTML page

The last family is the awkward one: V8 quotes the surrounding text but gives no position. The validator scans the text to find the spot itself, so you still get a line and caret; elsewhere, search your input for the quoted snippet. Python's json module words things differently (Expecting ',' delimiter, Extra data) but always gives a line and column.

Trailing commas

JSON5, JSONC and JavaScript allow a comma after the last item; JSON does not.

{
  "service": "billing-api",
  "port": 8080,
}

V8 reports Expected double-quoted property name at line 4, column 1: after a comma it wanted another key and found }. The validator recognises this pattern, highlights both lines and suggests removing the trailing comma on line 3. Delete it and the document parses:

{
  "service": "billing-api",
  "port": 8080
}

In an array, ["bom1", "fra1",] gets only Unexpected token ']' from V8; the validator still locates it and gives the same tip. If a template appends a comma after every item, fix the template: build the data structure and serialize it with JSON.stringify or json.dumps.

Single quotes, unquoted keys and Python literals

This is what you get when someone logs str(payload) in Python instead of json.dumps(payload):

{'service': 'billing-api', 'debug': True, 'parent': None}

It fails at line 1, column 2 with Expected property name or '}', and the validator adds a tip that JSON needs double quotes. Swapping the quotes is not enough: True, False and None must become true, false and null.

{"service": "billing-api", "debug": true, "parent": null}

Unquoted keys such as {service: "billing-api"} fail the same way, with a tip to quote the property name; they usually mean a JavaScript object literal was copied from source code or a console. Curly quotes are the third variant: JSON that passes through a word processor, a slide deck or a text field with smart punctuation comes back with “ ” instead of ", and fails the same way.

Comments

JSON has no comment syntax. Depending on where a // line sits, you get one of the first three messages in the table:

{
  "service": "billing-api",
  // keep in sync with the load balancer
  "port": 8080
}

Here it follows a comma, so V8 says Expected double-quoted property name at line 3, and the validator's tip names the comment as the cause. Before deleting anything, check what reads the file. tsconfig.json and VS Code's settings.json are JSONC, a dialect that accepts comments, so leave those alone. For real JSON, move the note into a field such as "_comment" if the consumer ignores unknown keys, or into documentation.

Missing or extra brackets and braces

Bracket errors surface where the parser noticed, sometimes lines after the mistake:

{
  "service": "billing-api",
  "regions": ["bom1", "fra1",
  "replicas": 3
}

The message is Expected ',' or ']' after array element at line 4, column 13. The parser happily read "replicas" as a third region, then choked on the colon. The fix is on line 3: change "fra1", to "fra1"],. Work backwards from the caret to the nearest [ or { that was never closed; your editor's bracket matching helps. One closing brace too many gives Unexpected non-whitespace character after JSON instead.

Raw newlines, tabs and backslashes in strings

RFC 8259 requires the control characters U+0000 to U+001F to be escaped inside strings. A message that spans two physical lines fails with Bad control character in string literal:

{"message": "Deploy failed:
missing env var"}

Escape the line break:

{"message": "Deploy failed:\nmissing env var"}

Tabs fail the same way; write \t. Backslashes are the other trap. The only valid escapes are \", \\, \/, \b, \f, \n, \r, \t and \uXXXX, so "C:\Users\ana" fails with Bad escaped character. "C:\new\temp" is worse: it parses without complaint as C:, a newline, ew, a tab and emp. Write "C:\\new\\temp" or use forward slashes. To escape a whole block of text, use the string escaper in JSON mode.

Values JSON does not have

NaN, Infinity, undefined, hex such as 0x1F, leading zeros such as 012, a leading + and bare decimals such as .5 are all invalid. V8 reports most of them as Unexpected token plus the first character, and 012 as Unexpected number.

{"ratio": NaN, "count": 3, "next": undefined}

Fix them where they are produced. Python's json.dumps writes NaN and Infinity by default and json.loads accepts them, so a Python service can round-trip them until a browser parses the response; pass allow_nan=False to fail at the source instead. JavaScript's JSON.stringify turns NaN and Infinity into null and drops undefined properties, so a literal undefined almost always means string concatenation, such as '{"next": ' + cursor + '}'. Decide whether the value should be null or absent, and let a serializer write it:

{"ratio": null, "count": 3}

More than one root value

A JSON text is exactly one value, optionally surrounded by whitespace. Log exports often contain one object per line:

{"event": "login", "user": 42}
{"event": "logout", "user": 42}

V8 says Unexpected non-whitespace character after JSON at position 31 (line 2 column 1); Python says Extra data. Each line is fine on its own: this is JSON Lines, also called NDJSON. Parse it line by line, or wrap it in an array with jq -s . events.ndjson.

Invisible characters and HTML pages

If the error is at the very first character and the text looks perfect, suspect a byte order mark: some Windows tools save UTF-8 with the bytes EF BB BF at the start. RFC 8259 says senders must not add one and parsers may ignore it, but JSON.parse doesn't. V8 reports Unexpected token with quotes that look empty; the validator names the BOM in its tip, and Python says Unexpected UTF-8 BOM (decode using utf-8-sig). To check a file:

$ head -c 3 config.json | xxd
00000000: efbb bf                                  ...

Strip it with text.replace(/^\uFEFF/, "") in JavaScript (Node's readFileSync(path, "utf8") keeps it) or open the file with encoding="utf-8-sig" in Python.

Finally, Unexpected token '<', "<!DOCTYPE "... is not valid JSON means you are parsing an HTML page, such as a proxy error, a 404 or a login redirect. Check the status and Content-Type before res.json().

Next steps

Paste the failing text into the JSON validator, fix the line it points at, and repeat until it reports valid JSON; then use the JSON formatter to read it. If your API still rejects valid JSON, the problem is shape, not syntax; JSON formatter vs JSON validator explains the difference.

Paste anything. devprix picks the tool.

Press ⌘K on any page, paste JSON, a JWT, a timestamp or a URL, and the right tool opens with your data filled in.