← Back to Blog

Why JSON.parse Throws: The Most Common Errors and How to Hunt Them Down

The most classic error message

SyntaxError: Unexpected token } in JSON at position 5

Every developer's first JSON.parse runs straight into it. The message is actually rich: it tells you which character index (position), which token went wrong, and that this is a syntax-level error, not a value-access problem. This article breaks down the four most common error classes and gives you a workflow you can actually use.

How to read the message

JSON.parse(arg) always throws SyntaxError, but two fields matter:

  • position: the zero-based index of the failing char; jump straight there in the string.
  • the token in the message: e.g. }, ,, o, 1—the char actually read at that spot.

Most parsers/validators draw an arrow to the failing point, which is exactly where you start.

Error class one: Unexpected token 'x'

JSON.parse('{"a":1,,}')   // Unexpected token ','
JSON.parse("{a:1}")       // Unexpected token 'a'   —— unquoted key
JSON.parse("{'a':1}")     // Unexpected token ' — single quotes

Standard JSON has three hard rules that heavy JavaScript use makes people forget:

  1. Keys must be double-quoted—no bare names, no single quotes;
  2. No comments (//, /* */ are illegal);
  3. No trailing commas—see below.

If a backend returns a "JSON-like but JS-style" string, fix it at the source rather than fighting it at parse time.

Error class two: Unexpected end of JSON input

JSON.parse('{"a":1')          // Unexpected end of JSON input
JSON.parse('')                // same error on empty string

This usually means the string is not complete: either you cut the tail, or a network/storage layer truncated it. Whenever you see "Unexpected end" near the end, suspect payload truncation before blaming a missing bracket.

Error class three: trailing comma

{"a":1,}   →  Unexpected token '}'
[1,2,]     →  Unexpected token ']'

JS object/array literals permit trailing commas, so it slips into JSON naturally. Trick: many online JSON formatters auto-append a trailing comma to objects; running that output through a strict validator misreports. Pick a "strip trailing commas" / no-comma option instead.

Error class four: Unexpected token o

const obj = {name:'k'};
JSON.parse(obj)       // Unexpected token o in JSON at position 0  ← 'o' of [object Object]
JSON.parse('123')     // valid! yields a number, no throw

The argument must be a string (or valid input that coerces to one). Parsing an already-parsed object turns it into [object Object] via toString, hence the o.

A three-step debugging workflow

When you get a soup of JSON, don't eyeball it—do this:

  1. Format: throw it into a JSON formatter; minified JSON becomes a tree/indented, and wherever the error arrow points becomes obvious. This is the most-skipped yet most effective step.
  2. Character audit: use find/replace to highlight risky patterns like ,\n, {\n, [\n; check for trailing commas, unquoted keys and leftover comments, and catch any stray HTML/log prefixes smuggled into the payload.
  3. Bisect: for payloads hundreds of KB+ failing near the end, parse the front and back halves separately to narrow the problem, and use the byte offset to check whether a multi-byte character was cut in half.

Why "it looks right" still fails

Three common illusions:

  • Editor/browser already converted it: paste auto-converts curly quotes/full-width colons to half-width, so you think it's fine.
  • An extra layer of serialization: the backend double-wrapped it, or the frontend stringified a string again—parsing yields a quoted string, not an object.
  • Escaping went wrong: \uXXXX escapes weren't restored, or newlines are literal (inside JSON strings they must be \n).

Self-check

Feed this broken example to a JSON validator/formatter and find three problems:

{
  name: "k",
  "tags": ["a", "b",],
  "note": "line break
  here"
}

If you can point out "unquoted key, trailing comma, literal newline in string" and predict each error token, JSON.parse errors stop being a mystery.

Reproducible Results

The four most common JSON.parse failures, reproduced one by one (JS wording differs; positions mean the same):

import json
json.loads("{'a': 1}")        # → Expecting property name enclosed in double quotes: char 1
json.loads('{"a": 1,}')       # → Illegal trailing comma before end of object: char 7
json.loads('{"a": tru}')      # → Expecting value: char 6
json.loads('{"a": "x" "y"}')  # → Expecting ',' delimiter: char 10

One unified reading: the reported char is where the parser got stuck — the single quote trips at char 1 (it expected "), the trailing comma right before }, and the misspelled literal (tru) where a value should start. Convert that offset to an editor column and 90% of JSON errors resolve in ten seconds. The full table is in the body.

Advertisement

Frequently Asked Questions

What does 'Unexpected token o' from JSON.parse mean?

It almost always means you parsed an object that was already an object: JSON.parse(object) calls toString() yielding [object Object], and o is its first letter. In other words you did not pass a string at all (an actual wrongly-typed string throws 'Unexpected token "x"' where x is the offending char). Fix: confirm the argument is really a string and parse only once—double serialization creeps in easily at storage/transfer boundaries.

Why does copying JSON into a parser report 'trailing comma'?

Standard JSON disallows trailing commas: both {"a":1,} and [1,2,] throw 'Unexpected token } or ]'. JavaScript object/array literals allow them, so people used to writing JS naturally produce illegal JSON. Some online JSON formatters also append a trailing comma, causing the 'my JSON looks fine but won't parse' illusion. Fix: run a formatter with a 'strip trailing commas' option, or delete the last comma and re-validate.

The JSON from the server keeps failing in the latter half, what do I do?

Suspect truncation first: a proxy/gateway cut the body, gzip decompression failed, or the endpoint returned a timeout artifact. To tell: save the raw body and run a 'JSON validator' to see where the error points—if it always fails near the end rather than near your own field, or reports 'Unexpected end of JSON input', truncation/untermination is the likely culprit. Fix: check server logs and the Content-Length/Content-Encoding headers for completeness; during dev, print and compare response.text().length against the declared length.

The string has Chinese/emoji, why does it throw 'Unexpected end'?

It is usually a byte/encoding issue, not the content itself: ①reading with the wrong charset (e.g. declaring utf-8 on latin-1 bytes) drops bits from some sequences; ②a payload truncated right in the middle of a multi-byte char yields a broken sequence after decoding. Standard JSON is UTF-8 and Chinese/emoji may appear raw (unescaped) inside strings, so their presence alone is nothing to fix. Instead: confirm the response is received fully as UTF-8, use a JSON validator to find the exact byte offset, and look backward to see which multi-byte character was cut.

← Back to Blog