Developer Guides

"Unexpected Token" in JSON: What It Means and How to Fix It

The token in the error message is the clue. Here's what "Unexpected token" means for each common character, from an HTML error page to a hidden BOM, and how to fix it.

  • By Jacob
  • 6 min read
A row of rounded tiles in white, cobalt and pale blue with one small cobalt tile knocked out of line below the others, next to a lime green bead

"Unexpected token" is JavaScript's way of saying it hit a character that can't appear at that point in JSON. The message tells you which character and roughly where, and that's usually enough to find the cause in a minute, once you know the handful of mistakes behind almost every case.

The examples below are the exact messages from Chrome and Node.js, which use the V8 engine. Firefox and Safari word them differently, but the causes are the same.

How to read the error message

A typical message from Chrome or Node.js looks like this:

SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

Two parts matter:

  • The token in quotes ('<' here) is the first character the parser couldn't accept.
  • The snippet after it shows the text around that point, or the position as a character count, as in in JSON at position 10. Positions count from 0.

Firefox reports line and column numbers instead, in the style of "JSON.parse: unexpected character at line 1 column 1 of the JSON data," as listed in MDN's reference for JSON.parse errors.

The token is the clue. Here's what each common one means.

Unexpected token '<': you got HTML, not JSON

This is the most common version, and the JSON itself is usually fine. The problem is that you never received JSON. A < at position 0 means the response starts with <!DOCTYPE html> or <html>: a web page.

SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

Typical causes:

  • The URL is wrong and the server returned its 404 page.
  • The request needs a login and you got the sign-in page.
  • The server crashed and returned an HTML error page (500).
  • A proxy, firewall or CDN returned its own block or challenge page.
  • In development, the dev server answered an unknown API path with your app's index.html.

The fix is to look at the raw response before parsing it. In the browser's developer tools, open the Network tab, click the request and check the status code and the Response tab. In code, check the status before you call .json():

const res = await fetch('/api/orders');
if (!res.ok) {
  throw new Error(`HTTP ${res.status}: ${(await res.text()).slice(0, 200)}`);
}
const data = await res.json();

MDN's page on Response.json() notes that the method parses the body as JSON and rejects if it can't, so an HTML body always ends up here.

Unexpected end of JSON input: the text is empty or cut off

SyntaxError: Unexpected end of JSON input

The parser ran out of text before the JSON was complete. Either the string was empty, or it stopped partway through an object or array. Common causes are a 204 No Content response, an empty file, a response cut off by a timeout, or code that parses before the data has loaded. For a truncated array, V8 may instead say Expected ',' or ']' after array element at the very last position.

Check that there's a body before parsing. If an empty response is normal for that endpoint, handle it explicitly instead of passing an empty string to JSON.parse.

Syntax that JavaScript allows but JSON doesn't

JSON looks like a JavaScript object literal, but its grammar, defined in RFC 8259, is much stricter. These are the usual mistakes, with V8's message for each:

MistakeExampleFix
Trailing comma["a", "b",]Remove the last comma
Single quotes{'id': 42}Use double quotes
Unquoted key{id: 42}Quote the key: "id"
Comment{"id": 42 // note}Remove it; JSON has no comments
Missing comma{"id": 42 "name": "Ana"}Add a comma between items
NaN or Infinity{"score": NaN}Use null or a number
Python values{"active": True, "note": None}Use true and null
Leading zero{"zip": 02134}Make it a string: "02134"
Raw line break in a stringA string split across two linesWrite \n instead

The messages you'll see for those, from Node.js 20:

Trailing comma:  Unexpected token ']', ..."["a", "b",],}" is not valid JSON
Single quotes:   Expected property name or '}' in JSON at position 1
Unquoted key:    Expected property name or '}' in JSON at position 1
Comment:         Expected ',' or '}' after property value in JSON at position 10
Missing comma:   Expected ',' or '}' after property value in JSON at position 11
NaN:             Unexpected token 'N', "{"score": NaN}" is not valid JSON
Python True:     Unexpected token 'T', ...""active": True, "not"... is not valid JSON
Leading zero:    Unexpected number in JSON at position 9
Line break:      Bad control character in string literal in JSON at position 18

Two of these deserve a note. A missing comma is reported where the parser noticed the problem, which is the start of the next item, so look just before the position it gives. And "Expected property name" doesn't tell single quotes from unquoted keys; look at the character at that position.

The RFC is explicit on the less obvious rules: in numbers, "leading zeros are not allowed"; values "such as Infinity and NaN are not permitted"; and inside strings, control characters (U+0000 through U+001F) must be escaped.

"undefined" and "[object Object]" are not valid JSON

SyntaxError: "undefined" is not valid JSON
SyntaxError: "[object Object]" is not valid JSON

These mean you passed something that wasn't a JSON string at all:

  • "undefined": the variable was undefined, often a missing property or a value read before it was set.
  • "[object Object]": you passed an object that was already parsed. JSON.parse turned it into the string [object Object] first. Remove the extra parse, or use JSON.stringify if you meant to go the other way.

An invisible character at the start

If a file looks perfect but fails at position 0, it may start with a byte order mark (BOM), the invisible character U+FEFF that some editors and Windows tools add to UTF-8 files. V8 shows it as a token that looks blank:

SyntaxError: Unexpected token '', "{"id": 42}" is not valid JSON

RFC 8259 says implementations "MUST NOT add a byte order mark" to JSON sent over a network, and that parsers "MAY ignore" one. JSON.parse doesn't, so strip it first, for example with text.replace(/^/, ''), or save the file as UTF-8 without BOM. Our JSON Formatter skips a leading BOM, so if a file formats fine there but fails in your code, a BOM is a likely suspect.

A quick debugging routine

  1. Log what you're parsing. Print the first couple of hundred characters and the type: console.log(typeof text, text.slice(0, 200)).
  2. If it starts with <, it's HTML. Check the status code and URL.
  3. If it's empty, check why the server sent no body.
  4. If it's JSON-like, paste it into the JSON Formatter. It points to the line and column of the first error and explains it in plain words, such as Trailing comma: remove the comma before "]" or True is not valid in JSON: write true in lowercase.
  5. Fix the producer, not just the copy. If a server or script keeps sending bad JSON, make it build the output with a real JSON serializer instead of gluing strings together.

For large responses, the JSON Viewer lets you fold and search the structure once the text parses, which helps when the data is valid but not what you expected.

When the JSON is valid but still wrong

Parsing successfully doesn't mean the data is right. Very large integers, such as 64-bit database IDs, lose precision in JavaScript because JSON.parse turns every number into a double: JSON.parse('{"id": 9007199254740993}').id gives 9007199254740992. Many APIs send such IDs as strings for this reason. If you're choosing a format for data exchange in the first place, JSON vs CSV vs XML compares the options.

FAQ

What does "Unexpected token < in JSON at position 0" mean?

The response is HTML, not JSON. Older Chrome versions used this wording; newer ones show the start of the text instead. Check the request's status code and URL.

Can JSON have trailing commas or comments?

No. Standard JSON allows neither. Some tools accept relaxed formats like JSONC or JSON5 for configuration files, but JSON.parse and most APIs don't.

Why does the error point to the wrong place?

The parser reports where it noticed the problem, not where you made it. A missing comma or quote is usually just before the reported position.

Is JSON with single quotes valid?

No. Strings and property names must use double quotes.