“Unexpected token < in JSON at position 0” means the text you tried to parse isn’t JSON at all. It’s almost always an HTML page, usually a 404, a 500 error page or a login redirect, returned where your code expected JSON. Look at the raw response in your browser’s Network tab, fix the URL or the server error, and the message goes away.
Newer versions of Chrome and Node word the same error differently: Unexpected token '<', "<!DOCTYPE "... is not valid JSON. Same cause, same fix. This guide covers that one first, then every other common JSON.parse error, with the exact messages a current Chrome shows.
What “position 0” tells you
The position is the character where the parser gave up, counting from 0. Position 0 means the very first character was wrong. Valid JSON can only start with {, [, ", a digit, -, true, false or null. So if the first character is <, you’re looking at HTML or XML.
A position further along, like 26, means the start was fine and something broke in the middle. That’s usually a typo in the JSON itself (see the table below).
Fixing “Unexpected token <“: find out what you actually received
A typical line that triggers it:
const res = await fetch('/api/users');
const data = await res.json(); // 💥 Unexpected token '<'
res.json() calls JSON.parse on whatever came back. To see what that was:
- Open DevTools (F12) → Network, reload, click the request and open the Response tab.
- Or log it in code before parsing:
const res = await fetch('/api/users'); const text = await res.text(); console.log(res.status, res.headers.get('content-type'), text.slice(0, 200));
You’ll almost always see one of these:
- A 404 page: the URL is wrong. Look for a missing
/apiprefix, a typo, a relative path resolving against the wrong base, or a trailing slash the server treats differently. - A 500 error page: the server crashed and its framework returned an HTML error page. Fix the server error. The JSON problem is just a symptom.
- A login page or redirect: your session expired or the request is missing its auth header or cookie, so the server sent you to sign in.
- Your app’s index.html: in single-page apps, the dev server returns
index.htmlfor any unknown path. That usually means the API proxy isn’t set up, or the request went to the frontend’s port instead of the API’s. - A firewall or CDN challenge page: bot protection answered instead of your API.
Make your code fail with a useful message
const res = await fetch('/api/users');
if (!res.ok) throw new Error(`HTTP ${res.status} from ${res.url}`);
const type = res.headers.get('content-type') || '';
if (!type.includes('application/json')) {
throw new Error(`Expected JSON, got ${type}: ${(await res.text()).slice(0, 100)}`);
}
const data = await res.json();
Now you get “HTTP 404 from https://…” instead of a confusing parse error.
Want to test the endpoint on its own, outside your app? Send the same request in the ToolBrainy API Tester and look at the status code and raw body. Our guide on testing a REST API online walks through it.
Every common JSON.parse error, and what causes it
These are the exact messages from Chrome 139, which uses the same engine as Node.js. Firefox words them differently (“JSON.parse: unexpected character at line 1 column 1”), but the causes are the same.
| Error message | What you passed | Fix |
|---|---|---|
Unexpected token '<', "<!DOCTYPE "... is not valid JSON |
An HTML page | Check the URL, status code and server logs (see above) |
Unexpected end of JSON input |
An empty string, or JSON cut off mid-way | The response body was empty (often a 204 or an error) or truncated. Check that there’s a body before parsing. |
"undefined" is not valid JSON |
The value undefined |
The variable was never set, for example reading a localStorage key that doesn’t exist. |
"[object Object]" is not valid JSON |
An object that’s already parsed | Don’t parse it again. It’s already JavaScript, or it was turned into a string without JSON.stringify. |
Expected property name or '}' in JSON at position 1 |
{'name': 'Ana'} or {name: "Ana"} |
JSON needs double quotes around every key and string. |
Expected double-quoted property name in JSON at position … |
{"a": 1, "b": 2,} |
Remove the trailing comma after the last property. |
Unexpected token ']', "[1, 2, 3,]" is not valid JSON |
A trailing comma in an array | Remove the comma before ]. |
Expected ',' or ']' after array element / Expected ',' or '}' after property value |
A missing comma, a comment, or truncated JSON | JSON has no comments, so remove // and /* */. Otherwise, look for the missing comma at the position given. |
Unexpected non-whitespace character after JSON |
{"a":1}{"b":2} |
Two JSON values glued together, often from appending to a file or streaming. Wrap them in an array, or parse one value per line. |
Bad control character in string literal |
A real line break or tab inside a string | Escape it as \n or \t, or build the JSON with JSON.stringify instead of by hand. |
Unexpected token 'N', "{"v": NaN}" is not valid JSON |
NaN or Infinity |
JSON has no NaN or Infinity. Use null or a string. |
Unexpected token '', "{…" is not valid JSON |
A file starting with an invisible byte order mark (BOM) | Save the file as UTF-8 without BOM, or strip it with text.trim() (in JavaScript, trim() removes a BOM too). |
Find the exact spot in a big JSON file
Newer error messages include the line and column, like (line 1 column 27), but minified JSON is all one line. The quickest way to find the problem is to paste it into the ToolBrainy JSON Formatter. It shows the parser’s error if the JSON is invalid. Once it’s valid, it formats the JSON with 2 spaces, 4 spaces or tabs so you can actually read it. It runs in your browser, so API responses and config files stay on your machine.
Prevent it next time
- Never build JSON by hand with string concatenation. Use
JSON.stringify(or your language’s equivalent). It handles quotes, escapes and commas for you. - Check
res.okbefore callingres.json(). Most “Unexpected token <” errors are really HTTP errors in disguise. - Return JSON errors from your API. Configure your server to answer API routes with
{"error": "…"}and a proper status code, not an HTML error page. - Wrap risky parses in try/catch when the input comes from users, files or
localStorage.
function safeParse(text, fallback = null) {
try { return JSON.parse(text); }
catch (e) { console.warn('Invalid JSON:', e.message); return fallback; }
}
Frequently asked questions
What does “Unexpected token < in JSON at position 0” mean?
The first character of the text you parsed was <, so it’s HTML, not JSON. Usually it’s an error page or a redirect returned by the server. Check the response in the Network tab.
Why does it work in Postman but fail in my app?
The requests are different. Compare the full URL, the method, the headers (especially Authorization and Content-Type) and the cookies. In the browser, a CORS preflight or a dev-server proxy can also change where the request actually goes.
What does “Unexpected end of JSON input” mean?
The parser ran out of text before the JSON was complete. Most often the response body was empty, for example a 204 No Content or an error with no body.
Does JSON allow comments or trailing commas?
No. Standard JSON allows neither. Some config formats (like JSON5, or the JSONC used in VS Code settings) do, but JSON.parse doesn’t accept them.
How do I validate JSON online?
Paste it into a JSON validator such as the ToolBrainy JSON Formatter. It shows the parser’s error if the JSON is invalid and formats it if it’s valid.
The short version
Position 0 and a < means you got HTML: check the URL, the status code and the auth. Any other position means the JSON itself is broken. Look for single quotes, trailing commas, comments or unescaped line breaks, and paste it into the JSON Formatter to see the error.
Handy tools for this topic
Free online tools, no signup
Every ToolBrainy tool runs right in your browser — no accounts, no watermarks and no limits. Compress a PDF, generate a strong password, convert an image and plenty more.



