How to Fix "Unexpected token < in JSON at position 0"
The ultimate guide to diagnosing why JSON.parse(), fetch(), and axios crash on HTML error responses.
01. Understanding the Error
When JavaScript throws SyntaxError: Unexpected token '<', ", it means your program attempted to execute:
JSON.parse("<!DOCTYPE html><html>..."); // 💥 CRASH: Character 0 is '<'
JSON documents must begin with an object brace {, an array bracket [, a string ", a number, a boolean, or null. The character < is never valid at position 0 in JSON.
02. Top 4 Reasons Your API Returns HTML
1. Incorrect URL or 404 Route
The endpoint path is misspelled (e.g. /api/users vs /api/user), causing the web server to return a 404 HTML page.
2. Server Crash (500 Error)
An unhandled backend exception caused the server framework (Express, Django, Laravel, Rails) to render an HTML stack trace.
3. Cloudflare or CDN Challenge (502 / 503)
A reverse proxy or WAF intercepted the request and returned a Cloudflare bot check, rate limit, or gateway timeout HTML page.
4. SPA Fallback / Catch-all Route
In React/Vue/Vite SPAs, unconfigured API proxy rules cause the dev server to return index.html for all requests.
03. Production-Ready Fix (JavaScript & TypeScript)
Always inspect response.ok and verify the Content-Type header before calling response.json():
async function safeFetchJson(url, options = {}) {
const response = await fetch(url, {
headers: {
'Accept': 'application/json',
...options.headers
},
...options
});
const contentType = response.headers.get('content-type') || '';
if (!response.ok || !contentType.includes('application/json')) {
const rawText = await response.text();
console.error('Server returned non-JSON response:', rawText.slice(0, 300));
throw new Error(`HTTP ${response.status}: Expected JSON, received ${contentType || 'unknown format'}`);
}
return await response.json();
}
Frequently Asked Questions
FAQ
What causes 'SyntaxError: Unexpected token < in JSON at position 0'?
This error occurs when your code expects JSON but receives HTML instead. The '<' character is the opening bracket of an HTML tag (such as '<!DOCTYPE html>' or '<html>') returned by a server error page (404, 500, or 502 Bad Gateway). How do I see what the server actually returned instead of JSON?
In browser JavaScript, read the response as text using 'const text = await response.text()' before parsing, or inspect the Network tab in Chrome DevTools to view the raw response payload. Why does this happen with fetch() or Axios in React/Next.js?
If your API route returns a redirect, 404 route mismatch, or unhandled server crash, the framework sends back an HTML error page. Calling 'response.json()' fails because HTML cannot be parsed as valid JSON. How can I prevent this error in production?
Always check 'if (!response.ok)' or inspect the 'Content-Type' header ('application/json') before calling 'response.json()'.
Explore Related Tools & Converters
100% Client-Side JSON Formatter Format, beautify and indent JSON payloads Open → JSON Validator Line-by-line syntax diagnostics & error locator Open → JSON Viewer Interactive tree explorer with path inspection Open → JSON Repair Fix trailing commas, quotes and unquoted keys Open → JSON Diff Semantic comparison ignoring key order Open → JSONPath Evaluator Query, extract and filter JSON structures Open → JSON Minifier Compress and compact JSON into single line Open → JSON to TypeScript Generate typed TypeScript interfaces Open → JSON to Python Generate Pydantic v2 & TypedDict models Open → JSON to Go Generate idiomatic Go structs with json tags Open → JSON to Rust Generate type-safe Serde structs Open → JSON to SQL Generate CREATE TABLE & INSERT statements Open → JSON to CSV Convert tabular JSON arrays to standard CSV Open → JSON to YAML Clean converter for configuration files Open →