
Fix "Unexpected token" in JSON.parse: a field guide to every cause
Published Jul 23, 2026
SyntaxError: Unexpected token X in JSON at position N is one of the most common errors in JavaScript, and one of the least helpful — it tells you where the parser gave up, not why. Here’s how to actually fix it.
Step 1: read the position number
JSON.parse counts characters from the start of the string, starting at 0. If your JSON came from a variable, log the exact slice around that position before doing anything else:
try {
JSON.parse(raw);
} catch (e) {
const match = e.message.match(/position (\d+)/);
if (match) {
const pos = Number(match[1]);
console.log(raw.slice(Math.max(0, pos - 20), pos + 20));
}
}
That one snippet solves more of these errors than any advice about JSON syntax, because most “unexpected token” errors are really “I don’t know what my string actually contains” errors — trailing whitespace from a copy-paste, an extra newline from a template literal, or a byte-order-mark (BOM) at the very start (position 0, token , which is invisible in most editors).
The 7 causes, ranked by how often they actually happen
- Trailing comma.
{"a": 1, "b": 2,}— valid in JS object literals, invalid in JSON. The comma before}or]is the single most common cause. - Single quotes instead of double quotes.
{'a': 1}is JS, not JSON. JSON requires double quotes around both keys and string values, no exceptions. - Unquoted keys.
{a: 1}— again, valid JS, invalid JSON. Every key needs quotes. - A JS value that isn’t valid JSON.
undefined,NaN, a function, or a comment (// like this) — none of these exist in the JSON spec, only in JavaScript. - Double-encoding. You called
JSON.stringify()twice, or you’re parsing a string that’s already an object — check withtypeof raw === 'string'before calling.parse(). - An empty string or
undefinedresponse. If a fetch failed silently or returned an empty body,JSON.parse("")throwsUnexpected end of JSON input, a different but related error — checkresponse.okand log the raw body before parsing. - BOM or invisible characters from copy-paste. Pasting JSON from a PDF, Word doc, or some terminal output can carry a BOM or non-breaking space that looks identical to a normal character.
The fastest fix: validate before you debug
Reading raw error messages character-by-character is slow. Paste the exact string into a formatter that highlights the invalid character directly instead of guessing from a position number — it turns “somewhere near character 812” into “this specific missing bracket, right here.” (Linked below.)
When the JSON is genuinely valid but still fails
If a formatter confirms your JSON is syntactically correct and JSON.parse still throws, the string you’re passing in isn’t the string you think it is. The most common reason: you’re calling .parse() on a Response object instead of its resolved body.
// Wrong — parses "[object Response]", not the body
const data = JSON.parse(await fetch(url));
// Right
const data = await (await fetch(url)).json();
// or, if you need the raw text first:
const text = await (await fetch(url)).text();
const data = JSON.parse(text);
Quick reference
| Symptom | Likely cause |
|---|---|
Unexpected token } |
Trailing comma before the closing brace |
Unexpected token ' |
Single quotes used instead of double quotes |
Unexpected token a (or any letter) |
Unquoted object key |
Unexpected end of JSON input |
Empty string, truncated response, or unclosed bracket |
Unexpected token (position 0) |
Byte-order-mark from a copy-pasted or Windows-saved file |
None of these require memorizing anything — the fix is always the same three steps: log the slice around the reported position, run it through a formatter, fix the one character it flags.