SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON — how to fix
Uncaught (in promise) SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
Quick fix: Open the request in DevTools → Network and look at the response: it's HTML, not JSON. Usually the API URL is wrong (404 → index.html), the server crashed (500 page), or you need to log in. Check res.ok before calling res.json().
What it means
JSON.parse (which response.json() uses) got text starting with <. JSON never starts with <, but HTML does: <!DOCTYPE html>. So the server sent a web page instead of JSON.
Find out what came back
DevTools → Network → click the request → Response. Or log it:
const res = await fetch('/api/items');
const text = await res.text();
console.log(res.status, text.slice(0, 300));
Common causes
1. Wrong URL, and a single-page app fallback serves index.html. Dev servers (Vite, CRA) and hosts like Netlify/Vercel return index.html for unknown paths, with status 200. Check the path, a missing /api prefix, or a missing dev-server proxy setting.
2. The API crashed and returned an HTML error page (500, 502 Bad Gateway).
3. Not logged in: you got redirected to an HTML login page.
4. Relative URL resolved differently: fetch('api/items') on /dashboard/ requests /dashboard/api/items. Use a leading slash: fetch('/api/items').
Make your code fail clearly
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status} for ${url}`);
const type = res.headers.get('content-type') || '';
if (!type.includes('application/json')) throw new Error(`Expected JSON, got ${type}`);
const data = await res.json();
Other "Unexpected token" variants
Unexpected token u in JSON at position 0— you parsedundefined(e.g.JSON.parse(localStorage.getItem('missing')), which is actuallynull… or an undefined variable). Check the value before parsing.Unexpected end of JSON input— the response body was empty (e.g. a 204 response). Don't call.json()on empty responses.Unexpected token o in JSON— you parsed something that is already an object ("[object Object]"). SkipJSON.parse.
Last updated 2026-10-27.