Developer Tools

JSON Errors Explained: How to Find and Fix Invalid JSON

Trailing commas, single quotes, comments, an invisible BOM: we fed 26 broken JSON snippets to Chrome, Python and PHP and wrote down what each one reports. Here is how to read the message and fix the real cause.

By Published 8 min read

JSON is a strict format: one comma in the wrong place and the whole file is rejected. The error messages do not help much either. Chrome says one thing, Python another, and PHP often just says "Syntax error". To find out what each message really means, we wrote 26 short snippets with the mistakes we see most often and parsed them with Chrome 141, Node.js 22 (both use the V8 engine, as does Microsoft Edge), Python 3.13 and PHP 8.3. You can paste your own JSON into our JSON formatter: it marks the line and column of the first error and names the likely cause.

What valid JSON looks like

JSON is defined in two documents that agree with each other: RFC 8259 from the IETF and ECMA-404. The rules fit on one screen:

  • A JSON text is exactly one value: an object {…}, an array […], a string, a number, true, false or null.
  • Strings and property names use double quotes. Single quotes and curly quotes are not allowed.
  • Items are separated by commas, but there is no comma after the last item.
  • There are no comments.
  • Numbers are decimal: no leading zeros (007), no hexadecimal (0xFF), no NaN or Infinity.
  • Inside strings, double quotes, backslashes and control characters such as line breaks and tabs must be escaped: \", \\, \n, \t.
  • Spaces, tabs and line breaks between items are allowed anywhere.

Many things that look like JSON are not: a JavaScript object literal, a Python dictionary printed with print(), a settings file with comments. They are close enough to fool the eye and far enough to fail every parser.

The same mistake, three different messages

This is what the three parsers reported for the most common mistakes. Chrome and Node.js gave identical messages in every case.

Chrome 141.0.7390, Node.js 22.22, Python 3.13.16 (json.loads), PHP 8.3.6 (json_decode with json_last_error_msg). Python also gives line, column and character for every error; PHP gives no position at all.
MistakeChrome / Node.jsPython 3.13PHP 8.3
Comma before }: {"age": 31,}Expected double-quoted property name in JSON at position 11 (line 1 column 12)Illegal trailing comma before end of object: line 1 column 11 (char 10)Syntax error
Comma before ]: [1, 2, 3,]Unexpected token ']', "[1, 2, 3,]" is not valid JSONIllegal trailing comma before end of arraySyntax error
Single quotes: {'name': 'Anna'}Expected property name or '}' in JSON at position 1Expecting property name enclosed in double quotesSyntax error
Missing comma between two propertiesExpected ',' or '}' after property value in JSONExpecting ',' delimiterSyntax error
// comment in the fileExpected property name or '}' in JSONExpecting property name enclosed in double quotesSyntax error
NaN as a valueUnexpected token 'N', … is not valid JSONaccepted without an errorSyntax error
Line break inside a stringBad control character in string literal in JSONInvalid control character atControl character error, possibly incorrectly encoded
Windows path "C:\Users\anna"Bad escaped character in JSONInvalid \escapeSyntax error
Two objects one after the otherUnexpected non-whitespace character after JSONExtra dataSyntax error
Empty inputUnexpected end of JSON inputExpecting value: line 1 column 1 (char 0)Syntax error
An HTML page instead of JSONUnexpected token '<', "<!DOCTYPE "... is not valid JSONExpecting value: line 1 column 1 (char 0)Syntax error

Two things stand out. Python is the most helpful: it names trailing commas and the byte order mark explicitly. PHP only says "Syntax error" for almost everything, so you have to find the spot yourself. And Python accepted NaN, which is not valid JSON. We come back to that below.

How to read an error message

When a message contains a position, it counts characters from zero, so "position 11" is the 12th character, which is why Chrome adds "line 1 column 12". Python counts lines and columns from one as well, but reports the comma itself (column 11). The position marks the place where the parser gave up, which is often a little after the actual mistake:

  • A comma before a closing bracket is reported at the bracket in Chrome, because the parser only notices the problem when it expects another property and finds } instead.
  • A missing comma is reported at the start of the next item, not at the end of the previous one.
  • A missing closing bracket is reported at the very end of the text, however far up the bracket is missing.

One detail for texts with emoji: JavaScript counts an emoji as two characters, so after an emoji Chrome's column is one too high. Python and our formatter count it as one.

Older browser versions use a different wording for the same errors. "Unexpected token < in JSON at position 0" is the classic one, and it means the same as the HTML row in the table: the text starts with <.

The most common causes and how to fix them

Trailing commas

JavaScript allows a comma after the last item of an object or array; JSON does not. They usually appear when lines are deleted or reordered by hand. Remove the comma before } or ].

Single quotes and curly quotes

JSON only accepts the straight double quote ". Curly quotes (“ ” or „ “) sneak in when JSON is copied through a word processor, a chat app or an e-mail program that "beautifies" quotes. Paste such text into a plain-text editor first or replace the quotes there.

JavaScript objects and Python dictionaries

Property names without quotes ({name: "Anna"}) are valid JavaScript but not JSON. In Python, printing a dictionary with print(data) or str(data) produces single quotes and True, False and None, all of which are invalid. Use JSON.stringify() in JavaScript and json.dumps() in Python, which always write real JSON.

Comments

Standard JSON has no comments. Some tools use extended formats that allow them, for example the "JSON with Comments" that Visual Studio Code uses for its settings, or tsconfig.json in TypeScript projects. That is why a file can work in one program and fail in another. Remove // … and /* … */ before sending the data to a standard parser.

NaN, Infinity and undefined

JSON has no way to write "not a number" or "infinite". JavaScript knows this: JSON.stringify() writes null instead. Python does not: json.dumps(float('nan')) writes NaN, which Python reads back without complaint but every browser rejects. Pass allow_nan=False to json.dumps() to get an error instead of invalid JSON. undefined is not a JSON value at all; JSON.stringify() simply leaves such properties out.

Numbers with leading zeros or a decimal comma

German postal codes such as 01067 (Dresden) or phone numbers starting with 0 are not numbers in the JSON sense. Write them as strings: "zip": "01067". The same goes for decimal commas: 0,5 is read as the number 0 followed by a comma, so write 0.5.

Line breaks and backslashes inside strings

A real line break or tab inside a string is not allowed; write \n and \t. A backslash starts an escape sequence, so a Windows path must double it: "C:\\Users\\anna", or use forward slashes, which Windows also understands in most places.

HTML where JSON was expected

If the error points to the very first character and mentions <, the server did not send JSON at all. It usually sent an HTML page: a 404 or 500 error page, a login page or a maintenance notice. Check the status code and the Content-Type header of the response before looking for a syntax error.

Invisible characters

Some editors and export functions save files as "UTF-8 with BOM": an invisible byte order mark (U+FEFF) at the start of the file. JSON.parse() rejects it, and Python's message even suggests the fix: decode the file with utf-8-sig. RFC 8259 says programs must not add a BOM to JSON they send. Our formatter removes it and tells you so.

Other invisible characters are just as fatal outside strings. In our tests a non-breaking space (U+00A0), which often comes along when you copy code from a web page or a document, was rejected by Chrome and Python alike, as were zero-width characters and the direction marks that right-to-left editors insert. Inside strings all of them are allowed. If the error points to a spot where you see nothing wrong, delete the space there and type it again.

More than one value

Two objects one after another are not one JSON text. Log files and data exports often use JSON Lines (also called NDJSON), with one object per line. Such files are fine, but each line has to be parsed on its own.

Valid, but still surprising

Some JSON is valid and still does not do what you expect:

Duplicate names: RFC 8259 says names SHOULD be unique and the result is unpredictable otherwise. All three kept the last value.
InputChrome / Node.jsPython 3.13PHP 8.3
{"a": 1, "a": 2}a is 2a is 2a is 2
{"id": 12345678901234567890}1234567890123456700012345678901234567890 (exact)1.2345678901234567E+19 (a float)

The second row matters in practice. JavaScript and PHP store numbers as 64-bit floating point values, which are only exact up to 9,007,199,254,740,991 (2^53 − 1). RFC 8259 recommends staying within that range. Large IDs, for example from databases or social networks, should therefore be sent as strings.

In the other direction, JSON.stringify() silently leaves out properties whose value is undefined or a function, turns NaN and Infinity into null and dates into strings. A BigInt value even throws an error ("Do not know how to serialize a BigInt").

Checklist when JSON is rejected

  1. Check that you really received JSON: a status code of 200 and a Content-Type of application/json.
  2. Paste the text into a formatter and look at the reported line and the character just before it.
  3. Look for the usual suspects: a trailing comma, single or curly quotes, a comment, an unquoted name.
  4. Check values: True/None from Python, NaN, numbers with leading zeros or decimal commas.
  5. Check strings: real line breaks, single backslashes in paths.
  6. Fix the program that produced the JSON so the error does not come back: use JSON.stringify(), json.dumps() or json_encode() instead of building JSON by hand.

JSON Formatter & ValidatorFormat, beautify, minify and validate JSON online. Shows the exact line and column of syntax errors. Your data stays in your browser.Open tool

Tools used in this guide

Frequently asked questions

Why does JSON not allow a comma after the last item?

The grammar in RFC 8259 and ECMA-404 simply has no rule for it, and the format is deliberately kept unchanged so that every parser reads the same data the same way. Extended formats such as JSON5 allow trailing commas and comments, but a standard JSON parser rejects them.

Can I put comments in JSON?

Not in standard JSON. Some programs accept comments in their own configuration files, for example Visual Studio Code's settings or TypeScript's tsconfig.json, but data you send to an API or read with JSON.parse() must not contain any. If you need notes, add them as a normal property such as "_comment".

What does "Unexpected token < in JSON at position 0" mean?

The text starts with "<", so it is almost certainly HTML, for example an error page or a login page, and not JSON. Newer Chrome versions phrase it as Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Check the address you called and the status code of the response.

Why does Python accept NaN when browsers reject it?

Python's json module reads and writes NaN, Infinity and -Infinity by default, using their JavaScript names, even though they are not part of the JSON standard. Use json.dumps(data, allow_nan=False) to make sure you only ever produce valid JSON.

Is the JSON I paste into the formatter uploaded anywhere?

No. Our JSON formatter runs entirely in your browser. The text is parsed and formatted on your device and is not sent to a server.

Why is my large ID number different after parsing?

JavaScript stores all numbers as 64-bit floating point values, which are exact only up to 9,007,199,254,740,991. Larger integers are rounded. Send such IDs as strings, for example "id": "12345678901234567890".

Sources and further reading

  1. RFC 8259 – The JavaScript Object Notation (JSON) Data Interchange Format (www.rfc-editor.org)
  2. ECMA-404 – The JSON data interchange syntax (2nd edition, 2017) (ecma-international.org)
  3. MDN Web Docs: JSON.parse() (developer.mozilla.org)
  4. Python documentation: json – JSON encoder and decoder (docs.python.org)
All guides