Common JSON Errors

Every common JSON parse error explained: Unexpected token, Unexpected end of JSON input, bad control character, and more — with the real cause and the fix.

8 min read

Read the error, then look one token earlier

JSON parse errors name the position where the parser gave up, which is usually one token after the mistake. A trailing comma is reported at the closing brace that follows it; an unclosed string is reported wherever the parser eventually gave up looking for the closing quote.

So the general method is: read the reported position, then look at the token immediately before it.

Paste your JSON and get the exact line →

The error table

ErrorReal causeFix
Unexpected token }Trailing comma after the last propertyDelete the comma before }
Unexpected token ]Trailing comma after the last elementDelete the comma before ]
Unexpected token 'Single-quoted string or keyUse double quotes
Unexpected token n (or another letter)Unquoted key from a JS object literalQuote the key
Unexpected end of JSON inputTruncated document, or empty stringCheck the input is complete
Unexpected token <The response is HTML, not JSONCheck the HTTP status and the URL
Bad control character in stringLiteral newline or tab inside a stringEscape as \n / \t
Unexpected token at position 0Byte order mark before the first characterRe-save as UTF-8 without BOM
Unexpected numberLeading zero, +, hex, or NaNUse a plain decimal
Unexpected token TTrue/NULL instead of true/nullJSON literals are lowercase
Duplicate key (strict parsers only)The same key twice in one objectRemove the duplicate

The five that account for most failures

1. Trailing comma

The most common JSON error, because modern JavaScript permits trailing commas and most linters actively encourage them.

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

The parser reads the comma after 2, expects a fifth token — another key — and finds }. Delete the comma.

Watch for this specifically in hand-edited config files, and in JSON assembled by string concatenation in a loop, where the separator is appended after every element including the last.

2. Single quotes

{'name': 'Ada'}

Valid JavaScript, invalid JSON. JSON permits double quotes only, for both keys and values. This nearly always comes from copying an object literal out of a .js file, or from Python's str(dict) — which produces single quotes and True/None. In Python, use json.dumps(obj), never str(obj).

3. Unquoted keys

{name: "Ada", age: 36}

Same root cause: JavaScript object literals allow bare identifiers as keys; JSON requires quoted strings. Every key needs double quotes.

4. HTML instead of JSON

Unexpected token < in JSON at position 0 means the body started with < — the beginning of <!DOCTYPE html>. The parser is working correctly; the request did not return JSON.

Usual causes: the endpoint 404'd and returned an error page, a session expired and a login page was returned, a proxy or CDN returned a notice, or the URL is simply wrong. Log the raw body and the status code before parsing:

const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);

const ct = res.headers.get("content-type") ?? "";
if (!ct.includes("application/json")) {
  throw new Error(`Expected JSON, got ${ct}: ${(await res.text()).slice(0, 200)}`);
}
return res.json();

5. The invisible ones

When JSON looks correct and still fails, the cause is usually a character you cannot see:

  • Byte order mark (BOM)U+FEFF before the first {. Windows editors add it when saving as "UTF-8 with BOM". Re-save as UTF-8 without BOM.
  • Smart quotes" and " instead of ". Introduced by Word, Google Docs, Notion, and chat clients. Never edit JSON in a word processor.
  • Non-breaking spaceU+00A0 instead of a normal space, usually from copying out of a web page.
  • Literal newline inside a string — must be escaped as \n.

A formatter reports the position of these even though your eyes cannot find them.

Language-specific gotchas

JavaScript. JSON.parse(undefined) throws "undefined" is not valid JSON — it stringifies the argument first. Guard before parsing. And JSON.stringify silently drops undefined values and functions from objects, so a round-trip is not always lossless.

Python. str(dict) produces {'a': True, 'b': None} — single quotes and capitalised literals, none of which is JSON. Always use json.dumps. Note also that Python's encoder writes NaN, Infinity, and -Infinity by default, which are not valid JSON; pass allow_nan=False to raise instead.

PHP. json_encode returns false on failure rather than throwing. Check json_last_error_msg(), or pass JSON_THROW_ON_ERROR.

Java / Jackson. Rejects unknown properties by default, producing UnrecognizedPropertyException on JSON that is perfectly valid but has extra fields. Configure FAIL_ON_UNKNOWN_PROPERTIES to false when consuming third-party APIs.

Debugging large files

Character offsets are useless in a 40,000-line file. Get a line number instead:

jq . large.json                  # reports line and column
python -m json.tool large.json   # same, no dependency

# Newline-delimited JSON: find the offending line
jq -e . < data.ndjson > /dev/null || echo "invalid line above"

For files too large to inspect by eye, bisect: split the file in half and validate each part until you isolate the failing region.

Preventing errors instead of fixing them

  • Generate JSON with a serialiser, never string concatenation. Every trailing-comma bug comes from building JSON by hand.
  • Validate before writing — parse the string you just produced; if it throws, you caught it before it shipped.
  • Check content-type and status before parsing a response, as above.
  • Use a schema for anything crossing a system boundary. JSON Validation covers JSON Schema.
  • Format files on commit so malformed JSON never reaches the repository.

Related

Frequently asked questions

What does "Unexpected token } in JSON at position N" mean?

Almost always a trailing comma — a comma after the last property of an object or the last element of an array. The parser reads the comma, expects another value, and finds a closing brace instead. Delete the comma before the closing } or ]. Modern JavaScript allows trailing commas, which is why this is the most frequent JSON error.

What does "Unexpected end of JSON input" mean?

The document ended while something was still open — an unclosed brace, bracket, or quote. Common causes are a truncated file, a network response cut short, a read that finished before the stream completed, or an empty string passed to JSON.parse. Check that the input is complete and that braces and brackets are balanced.

Why do I get "Unexpected token < in JSON at position 0"?

The response was HTML, not JSON — the "<" is the start of <!DOCTYPE or <html>. This normally means the request hit an error page, a login redirect, a 404, or a proxy notice rather than the API. Inspect the raw response body and the HTTP status code; the JSON parser is reporting a networking or routing problem, not a data problem.

Why does my valid-looking JSON fail to parse?

The usual causes are invisible or easy to overlook: a byte order mark (BOM) before the first character, a smart quote (" or ") substituted by a word processor, a non-breaking space, a literal newline inside a string, or single quotes copied from JavaScript. Paste the document into a formatter — it will report the exact line and column.

How do I find which line of a large JSON file is broken?

Use a validator that reports line and column rather than only a character offset. On the command line, jq . file.json prints the position of the failure, and python -m json.tool file.json does the same. For a quick check, paste the document into a browser-based formatter that reports the line number directly.

More in JSON Guide

← Back to JSON Guide