↑↓ to move ↵ to open Esc to close Browse all tools

Developer guide

JSON Syntax Errors: What Breaks a Parser and How to Fix It

JSON looks like JavaScript, but its grammar is much stricter, and one stray comma makes a whole file unreadable. The rules fit on a page and come from two short standards, RFC 8259 and ECMA-404. Here is what valid JSON is, the usual errors, each with a broken and a fixed snippet, and how to read the position in the error message.

What valid JSON is

RFC 8259 and ECMA-404 describe the same syntax. ECMA-404, now in its 2nd edition from December 2017, defines only that syntax and nothing about what the data means. A JSON text is one value, and a value is one of six things:

  • an object: { then zero or more "name": value members separated by commas, then }
  • an array: [ then zero or more values separated by commas, then ]
  • a string in double quotes
  • a number: an optional minus, digits, an optional fraction, an optional exponent
  • one of three literal names, true, false or null, which the RFC says must be lowercase

Whitespace may appear around the structural characters, and only four characters count as whitespace: space, tab, line feed and carriage return. Since RFC 8259, the top level does not have to be an object or an array; "hi" and 42 are complete JSON texts. The RFC also allows a parser to accept extensions, which is why some tools take input that others reject. A strict parser such as JSON.parse in a browser does not.

The common errors at a glance

ErrorExampleWhy it failsFix
Trailing comma[1, 2, 3,]A comma must be followed by another value or memberDelete the last comma
Single quotes{'a': 1}Strings start and end with double quotes onlyUse " for keys and strings
Unquoted key{a: 1}A member name is a string, so it needs quotesQuote every key
Comment// noteThe grammar has no comment syntaxRemove it or move it into data
NaN or Infinity{"x": NaN}Not allowed as numbersUse null or a string
Leading zero{"n": 007}Leading zeros are not allowedWrite 7, or "007" as a string
Bare decimal point{"n": 1.}A point needs at least one digit after itWrite 1.0 or 1
Raw tab or newline in a string"line 1⏎line 2"U+0000 to U+001F must be escapedWrite \n, \t, \r
Capitalized literal{"ok": True}true, false and null must be lowercaseWrite true, false, null
undefined{"x": undefined}Not a JSON valueUse null or drop the key
Byte order markU+FEFF before [Not one of the four whitespace charactersSave as UTF-8 without BOM

The fastest check is to paste the text into the JSON Formatter, which parses it, reports the first error and pretty-prints the result once it is valid. The sections below show each error in full.

Trailing commas

In the grammar a comma is a separator: it must sit between two values or two members. A comma right before ] or } leaves the parser expecting another item that never comes. It slips into hand-edited files because JavaScript, Python and many config formats allow it.

Broken:  {"name": "Ada", "langs": ["en", "fr",],}
Fixed:   {"name": "Ada", "langs": ["en", "fr"]}

Single quotes and unquoted keys

A string begins and ends with a double quotation mark, and an object member name is a string. So 'Ada' is not a string and name without quotes is not a key. Both come from copying a JavaScript object literal. Apostrophes inside a double-quoted string are fine and need no escape; a double quote inside one must be written \".

Broken:  {name: 'Ada', 'city': 'Paris'}
Fixed:   {"name": "Ada", "city": "Paris"}
Fixed:   {"quote": "She said \"hi\" and didn't stop"}

Comments

There is no comment syntax in JSON, neither // nor /* */. MDN lists comments among the JavaScript features JSON leaves out. If notes have to live in the file, make them data. If people edit the file often, write it in YAML, which has comments, and convert it with YAML to JSON when a program needs JSON. The guide JSON vs YAML vs CSV compares the formats.

Broken:  {
           "port": 8080  // dev only
         }
Fixed:   {
           "port": 8080,
           "_comment": "dev only"
         }

Numbers: NaN, Infinity, leading zeros

RFC 8259 states that numbers the grammar cannot represent, such as Infinity and NaN, are not permitted, and that leading zeros are not allowed. MDN adds that a decimal point must be followed by at least one digit. A plus sign is allowed only in the exponent, as in 1e+3, never in front of the number.

Broken:  {"ratio": NaN, "zip": 02134, "price": 5., "delta": +2}
Fixed:   {"ratio": null, "zip": "02134", "price": 5.0, "delta": 2}

A ZIP code or an ID with a leading zero is really text, so store it as a string. NaN usually sneaks in through code: JSON.stringify does not fail on it but, as MDN documents, writes NaN and Infinity as null. The output is valid JSON, but the number is gone, so check for it before serializing if the value matters.

Control characters inside strings

The RFC requires the control characters U+0000 through U+001F to be escaped inside a string. That range includes tab, line feed and carriage return, so a string that spans two lines in the file, or contains a literal tab pasted from a spreadsheet, is invalid. Write the escape sequences instead: \n for a line feed, \t for a tab, \r for a carriage return, \\ for a backslash and \uXXXX for any other code point.

Broken:  {"address": "1 Main St
         Springfield"}
Fixed:   {"address": "1 Main St\nSpringfield"}

Windows paths trip on the same rule from the other side: "C:\new" is read as C, colon, line feed, e, w. Double the backslash: "C:\\new". Text Escape and Unescape converts a block of text with quotes, backslashes and line breaks into a valid JSON string in one step.

Literals, undefined and the byte order mark

True, False and None are Python spellings, not JSON. The literal names must be lowercase and there are only three, so undefined is not a value either. Use null, or leave the key out.

A byte order mark is the invisible character U+FEFF that some editors put at the start of a UTF-8 file. RFC 8259 says implementations must not add one to JSON sent over a network, and that a parser may ignore one rather than treat it as an error. JSON.parse does not ignore it: U+FEFF is not one of the four whitespace characters, so a string that starts with it fails at position 0 even though the text looks perfect. Save the file as UTF-8 without BOM, or strip a leading \uFEFF before parsing.

Reading the error position

JSON.parse throws a SyntaxError when the text is not valid JSON. The message wording depends on the browser engine. MDN's example of a trailing comma reads:

JSON.parse("[1, 2, 3, 4, ]");
// SyntaxError: Unexpected token ] in JSON at position 13

The position is a zero-based count of characters from the start of the text. In [1, 2, 3, 4, ] the closing bracket is at index 13, the 14th character, right after the comma that caused it. Firefox reports a line and a column instead, in the form at line 1 column 2 of the JSON data, which is easier to find in a long file.

The position marks where the parser gave up, which is not always where the mistake is. MDN's leading zero example, {"foo": 01}, fails with "expected ',' or '}' after property value": the parser accepted 0 as a complete number and then choked on the 1. A missing comma between two members likewise shows up at the start of the second one. When the marked spot looks correct, read the few characters just before it.

Once it parses

  • Check for duplicate keys. The RFC says names within an object should be unique but does not forbid duplicates, and parsers differ on which one wins.
  • Check the types. "42" and 42 are both valid but not the same. JSON to TypeScript turns a sample into type definitions, which makes a string where a number should be easy to spot.
  • Generate JSON, do not hand-write it. Build the data as objects in code and serialize it with the language's JSON library. Every error in the table above comes from text typed or pasted by hand.

Frequently asked questions

Why does my JSON work in JavaScript but not in JSON.parse?

Because a JavaScript object literal allows things JSON does not: single quotes, unquoted keys, trailing commas, comments, undefined and NaN. MDN puts it plainly: JSON is based on JavaScript syntax, but most of JavaScript is not JSON. Paste the text into a validator and it will show the first character that breaks the stricter grammar.

Can JSON have comments?

No. The grammar in RFC 8259 has no comment syntax, and MDN lists comments among the JavaScript features JSON does not support. If a file needs notes, store them as data under a key, or write the file in YAML, which has comments, and convert it to JSON when a program needs it.

Is a trailing comma allowed in JSON?

No. After a comma the grammar expects another value in an array or another name and value pair in an object. A comma right before ] or } is a syntax error in every conforming parser, even though JavaScript, Python and many config formats accept it.

What does "at position 13" mean in a JSON error?

It is a zero-based character offset into the text, counted from the first character. Position 13 is the 14th character. In MDN's example "[1, 2, 3, 4, ]" that is the closing bracket right after the trailing comma. Other engines report a line and column instead, which is easier to use in a long file.

Is a plain string or number valid JSON on its own?

Yes. RFC 8259 defines a JSON text as a single serialized value of any type, so "hello", 42 and null are all valid documents. Older specifications required the top level to be an object or an array, which is why some old tools still reject a bare value.

Sources