Skip to content

Invalid JSON — the most common mistakes and how to find them fast

Trailing commas, single quotes, comments, unquoted keys and other reasons a JSON document is rejected, with the exact fix for each one.

By Felipe YamaokaPublished November 5, 20265 min read

"Unexpected token" at the worst moment

A deploy fails because a configuration file does not load. An API returns 400 and the only clue is Unexpected token } in JSON at position 1843. You open the file, it looks fine, and you start counting braces.

JSON is a deliberately small format, and that is exactly why it rejects things that look harmless to people who also write JavaScript, Python or YAML. Almost every invalid document falls into one of a handful of mistakes. This guide lists them, shows what each one looks like and how to fix it, so the next error message takes seconds instead of minutes.

What JSON actually allows

The whole format, defined in RFC 8259, fits in a few rules:

  • A value is an object, an array, a string, a number, true, false or null.
  • Object keys are always strings in double quotes.
  • Strings use double quotes only, and some characters inside them must be escaped.
  • Items in objects and arrays are separated by commas, and there is no comma after the last one.
  • Numbers are decimal: no leading zeros, no NaN, no Infinity, no hexadecimal.
  • There are no comments.

Everything below breaks one of these rules.

1. Trailing commas

The most frequent mistake by far, because JavaScript, TypeScript and Python all accept it:

json
{
  "name": "api",
  "port": 8080,
}

Remove the comma after the last item. The same applies to arrays: [1, 2, 3,] is invalid. The JSON Formatter reports it as "Trailing commas are not allowed" and points to the line and column of the closing brace, which is where a parser realizes something is wrong.

2. Single quotes

json
{ 'name': 'api' }

JSON strings and keys need double quotes. Replace ' with ". If the text itself contains a double quote, escape it: "He said \"hi\"".

3. Unquoted keys

json
{ name: "api" }

This is valid JavaScript but not JSON. Every key must be a quoted string: { "name": "api" }.

4. Comments

json
{
  "port": 8080 // default port
}

JSON has no comments, neither // nor /* */. Files like tsconfig.json and the VS Code settings accept them because those tools use a relaxed variant called JSONC, but a standard parser, such as JSON.parse or most APIs, rejects them. If you need notes in configuration, use a field like "_comment" or switch to a format that supports comments, such as YAML or TOML.

5. Invalid numbers

All of these are rejected:

text
{ "a": 01 }        leading zero
{ "a": .5 }        missing digit before the point
{ "a": NaN }       not part of JSON
{ "a": Infinity }  not part of JSON
{ "a": 0x1F }      no hexadecimal

Write 1, 0.5 and 31. For values that JSON cannot express, such as NaN, use null or a string and handle it in code.

A related trap is precision, not validity. 12345678901234567890 is valid JSON, but JSON.parse in JavaScript turns it into 12345678901234567000, because numbers above 2 to the power of 53 lose digits as doubles. IDs from other systems, such as tweet IDs or bank references, should travel as strings. The JSON Formatter on this site keeps the original digits when it reformats, so it is a safe way to look at such payloads.

6. Line breaks and control characters inside strings

text
{ "message": "first line
second line" }

A raw line break inside a string is not allowed. Use the escape \n: "first line\nsecond line". The same goes for tabs (\t) and other control characters. This usually appears when someone builds JSON by concatenating strings instead of using a serializer.

7. Content after the end

json
{ "a": 1 }}

or two documents glued together:

text
{ "a": 1 }
{ "b": 2 }

A JSON document has exactly one top-level value. An extra brace usually comes from a bad edit. Several objects one per line is a different format, JSON Lines (NDJSON), which must be read line by line.

8. The document ends too early

text
{ "items": [1, 2, 3

A missing ] or } at the end, often because a log line was truncated or a response was cut. The error position will be the very end of the text. Check whether you have the whole payload before hunting for a typo.

How to find the error fast

  1. Read the position, not just the message. Parsers report where they gave up, which is usually just after the real mistake. Look at the line before.
  2. Format the document. Indentation makes a missing comma or an extra brace obvious. A formatter that shows "line 12, column 3" saves you from counting characters.
  3. Generate JSON, do not write it by hand. JSON.stringify in JavaScript, json.dumps in Python and the serializers of every language never produce these mistakes.
  4. Validate at the boundary. When your code receives JSON, parse it once, report the error with its position and stop, instead of letting a half-read object travel through the system.

In JavaScript, a safe parse with a clear message looks like this:

js
function parseJson(text) {
  try {
    return { ok: true, value: JSON.parse(text) };
  } catch (error) {
    return { ok: false, message: error.message };
  }
}

Common mistakes, in one list

  • A comma after the last item of an object or array.
  • Single quotes instead of double quotes.
  • Keys without quotes.
  • Comments copied from a JSONC or JavaScript file.
  • Numbers with leading zeros, NaN or Infinity.
  • Raw line breaks inside strings.
  • Extra content after the closing brace.
  • Truncated documents.

Summary

JSON is strict on purpose: double quotes everywhere, no trailing commas, no comments and plain decimal numbers. When a parser complains, read the line and column, look just before that point and check the list above; nine times out of ten the fix is one character. And whenever you can, let a serializer write the JSON for you.

Keep reading