SyntaxError: Unexpected token '<' in JSON — HTML returned instead of JSON
Why your JSON parser sees '<' at position 0, how to detect that your server sent an HTML error page instead of JSON, and the exact fetch pattern that prevents it.
The error "SyntaxError: Unexpected token '<' in JSON at position 0" means the very first byte of the string you handed to JSON.parse is a less-than sign. JSON cannot start with '<'. The server sent HTML — an error page, a login redirect, or a misconfigured endpoint — instead of JSON.
Why this happens
HTTP responses have a Content-Type header. When the server returns JSON it should send Content-Type: application/json. But when something goes wrong server-side (authentication error, 404, 500, nginx misconfiguration), many frameworks return an HTML error page that starts with <!DOCTYPE html> or <html>. If your client calls .json() on that response without checking the status code first, you get the '<' error.
The fix: always check response.ok
// ✗ common mistake — calls .json() on any response
const data = await fetch('/api/users').then(r => r.json());
// ✓ check HTTP status before parsing
const res = await fetch('/api/users');
if (!res.ok) {
const text = await res.text(); // read the actual error body
throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);
}
const data = await res.json();How to debug it in < 30 seconds
- Open DevTools → Network tab → find the failing request → click it → check the Response tab. If you see HTML there, the problem is server-side.
- Check the HTTP status code. 401 (auth), 403 (forbidden), 404 (wrong URL), 500 (server crash), and 502/503 (gateway errors) all commonly return HTML.
- Add a temporary
console.log(await res.text())before the.json()call to see the raw body.
Server-side checklist
- Confirm the route exists and the URL is correct — a missing route returns a 404 HTML page.
- Check that authentication middleware is configured to return JSON errors, not HTML redirects.
- Verify the server is actually running — a proxy with no upstream returns HTML.
- In development, check for port mismatches (app on :3000, fetching from :3001).
Paste your JSON into the validator →
Get the exact line, column, and a fix hint in seconds — no upload, no signup.
Open JSON Validator →