Fix: Invalid JSON Response from API
When an API call returns an HTML error page instead of JSON, calling res.json() or JSON.parse() throws a SyntaxError. Here is how to detect, handle, and debug it.
The Error Message
The most common form of this error looks like:
SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSONThe < is the first character of an HTML page. The server returned something like a 404 page, a login redirect, a WAF block page, or a 500 error template — none of which are JSON.
Common Causes
| HTTP Status | Cause | Response Body |
|---|---|---|
| 404 Not Found | Wrong URL or endpoint removed | HTML 404 page |
| 500 Internal Server Error | Unhandled server exception | HTML error template |
| 401 Unauthorized | Missing or expired auth token | HTML login page or error |
| 302 Redirect | Session expired, CORS issue | HTML redirect or login page |
| 200 OK | Server misconfigured to return HTML | HTML with wrong Content-Type |
| 503 Service Unavailable | Server down or maintenance | HTML maintenance page |
Safe fetch() Pattern
Check res.ok and the Content-Type header before calling res.json():
async function fetchJson<T>(url: string): Promise<T> {
const res = await fetch(url);
// Non-2xx status: read body as text to expose the error
if (!res.ok) {
const body = await res.text();
throw new Error(
`HTTP ${res.status} ${res.statusText}: ${body.slice(0, 200)}`
);
}
// Guard against wrong Content-Type (e.g. accidental HTML response)
const contentType = res.headers.get("content-type") ?? "";
if (!contentType.includes("application/json")) {
const body = await res.text();
throw new Error(
`Expected JSON but got ${contentType}: ${body.slice(0, 200)}`
);
}
return res.json() as Promise<T>;
}Axios Pattern
With Axios, non-2xx responses automatically throw, so you just need to handle the error:
import axios, { AxiosError } from "axios";
try {
const { data } = await axios.get("/api/users");
console.log(data);
} catch (err) {
if (err instanceof AxiosError && err.response) {
// err.response.data is the parsed body (may be HTML string)
console.error("Status:", err.response.status);
console.error("Body:", String(err.response.data).slice(0, 200));
} else {
throw err;
}
}Debug the Raw Response
If you are not sure what the server is returning, fetch the body as text and paste it below to inspect it:
const res = await fetch("/api/data");
const raw = await res.text();
console.log(raw); // paste this into the formatter belowOr open the browser DevTools → Network tab → click the request → Response tab to see the raw body.
Frequently Asked Questions
Why does JSON.parse() throw 'Unexpected token <'?▾
The < character is the start of an HTML tag. This error means the server returned an HTML page (often a 404 Not Found or 500 Internal Server Error page) instead of JSON. JSON.parse() immediately fails because < is not a valid JSON character at position 0.
How do I check if a fetch() response is JSON before parsing?▾
Check two things: (1) res.ok — true only for 2xx status codes. (2) res.headers.get('content-type') — should contain 'application/json'. If either check fails, read the body as text with res.text() to see the actual error message from the server.
Does fetch() throw an error on 404 or 500 responses?▾
No. fetch() only rejects (throws) on network failures like DNS errors or CORS blocks. HTTP error status codes like 404, 500, or 401 still resolve successfully — res.ok will be false and res.status will be the error code, but no exception is thrown. You must check res.ok manually.
How does Axios handle non-JSON responses differently?▾
Axios throws an AxiosError for any non-2xx response, so 404 and 500 automatically land in the catch block. The error object has error.response.data (parsed body), error.response.status, and error.response.headers. This makes it easier to inspect the HTML error page without additional checks.
How can I see the raw response to debug an invalid JSON error?▾
Use the browser DevTools Network tab to inspect the raw response body — look at the Response tab for the request. Alternatively, call res.text() instead of res.json() and console.log the raw string. Then paste it into the JSON formatter on this page to identify what the server actually returned.
More JSON error fixes
See our other error fix guides below.
If jsondecode.com saved you time, share it with your team
Free forever. No ads. No sign-up. Help other developers find it.