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

This error means the text you tried to parse is not JSON. The token it names usually tells you exactly what you received instead.

You call JSON.parse() or response.json() and get a SyntaxError instead of data. The wording depends on the browser, but the meaning is always the same: the parser read the text from the start and hit a character that cannot appear there in valid JSON. The character it quotes, and the position, are the most useful clues you have.

The same error in different engines

Newer versions of Chrome and Node.js changed the wording, so you will find both versions in search results and in older logs.

Where you see itMessage
Chrome, Edge, Node.js 20 and laterUnexpected token '<', "<!DOCTYPE "... is not valid JSON
Older Chrome and Node.js 18Unexpected token < in JSON at position 0
FirefoxJSON.parse: unexpected character at line 1 column 1 of the JSON data
SafariJSON Parse error: Unrecognized token '<'

"Position 0" or "line 1 column 1" means the very first character was already wrong. That almost always points to the response being something other than JSON, rather than a small typo inside a JSON document.

The usual cause: an HTML page instead of JSON

An unexpected < at position 0 is by far the most common version. The code asked an API for JSON, but the server answered with an HTML page, and HTML starts with <!DOCTYPE html> or <html>. Typical reasons:

  • A wrong URL: a typo or a missing /api prefix returns the site's 404 page.
  • A server error: the framework renders an HTML 500 error page.
  • An expired session: the request is redirected to a login page, and fetch follows the redirect silently.
  • A single-page app fallback: the dev server or hosting returns index.html for any unknown path, with status 200.
  • A proxy, CDN or firewall in between sends its own HTML error or challenge page.

The quickest way to confirm it is to look at the raw response before parsing it:

const res = await fetch('/api/items');
console.log(res.status, res.headers.get('content-type'));
const text = await res.text();
console.log(text.slice(0, 200)); // shows the HTML if that is what came back

In the browser you can also open the Network panel, click the request and check the Response tab. If you see HTML, fix the URL, the authentication or the server error; the parsing code itself is fine.

Other tokens and what they point to

Current message (older message)What you parsedFix
"undefined" is not valid JSON (Unexpected token u in JSON at position 0)The value undefined, for example a missing localStorage key or an unset variableCheck that the value exists before parsing it
"[object Object]" is not valid JSON (Unexpected token o in JSON at position 1)An object that was already parsed, turned into a string by JSON.parseUse the object directly; do not parse it twice
Expected property name or '}' in JSON at position 1Single quotes or unquoted keys, as in {'a': 1} or {a: 1}Use double quotes around every key and string
Expected double-quoted property name in JSON at position 8A trailing comma, as in {"a": 1,}Remove the comma before the closing brace
Unexpected token 'N', "{"a": NaN}" is not valid JSONNaN, Infinity or undefined inside the dataReplace them with null or a number
A message that quotes an invisible or empty-looking token at position 0A byte order mark at the start of a file saved by some Windows editorsSave the file as UTF-8 without BOM, or strip the first character

Parsing data that is already parsed

Libraries often hand you ready-made objects. response.json() already returns a JavaScript object, and so does axios in response.data. Passing that object to JSON.parse converts it to the string "[object Object]" first, which fails at position 1. Remove the extra parse:

const data = await res.json();
// JSON.parse(data)  <- fails: data is already an object
console.log(data.items);

Finding a mistake inside real JSON

When the error position is somewhere in the middle, the text really is JSON with a mistake in it. Paste it into the JSON Validator: it uses the same parser as your browser, shows the message with its position and line, and formats the document as soon as it is valid. The actual mistake is usually just before the reported position, such as a missing comma between two items or a quote that should have been a double quote.

Guarding against it in your code

Checking the status and content type before parsing turns a confusing syntax error into a message that says what really happened:

async function getJson(url) {
  const res = await fetch(url);
  const type = res.headers.get('content-type') || '';
  if (!res.ok || !type.includes('application/json')) {
    const body = await res.text();
    throw new Error(`Expected JSON from ${url}, got ${res.status} ${type}: ${body.slice(0, 100)}`);
  }
  return res.json();
}

The same idea applies on the server side: return JSON error bodies from API routes instead of HTML error pages, and send Content-Type: application/json with them. For the closely related message you get when the body is empty or cut off, see Unexpected end of JSON input.

Frequently Asked Questions

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

The response started with a < character, which means it is HTML, usually an error page, a login page or an index.html fallback, not JSON. Check the request URL, the status code and the Content-Type header.

Why does it say "[object Object]" is not valid JSON?

You passed an object to JSON.parse. It was converted to the text [object Object] first. The value is already parsed, so use it directly.

Can a JSON validator find the problem?

Yes, if the text is meant to be JSON. A validator shows the exact position of the first error. If the text is HTML, the fix is on the server or in the request, not in the JSON.