JSON Errors / JavaScript (Safari / React Native)

SyntaxError: JSON Parse error: Unrecognized token '<'

How to Fix: SyntaxError: JSON Parse error: Unrecognized token '<'

This is how JavaScriptCore - the engine in Safari, iOS web views, and React Native apps running on JSC - reports that JSON.parse() or response.json() hit a character that cannot start a JSON value. The character is quoted: '<' means the text is HTML, and an apparently empty quote (Unrecognized token '') usually hides an invisible byte-order mark.

Chrome and Node report the same problem as "Unexpected token '<', "<!DOCTYPE "... is not valid JSON". JavaScriptCore has its own family of messages for related cases: "Unexpected EOF" for an empty response, and "Unexpected identifier "undefined"" for a value that was never JSON.

{"find": "error"}paste your JSON below

Common causes

1. Unrecognized token '<': the server returned HTML

An error page, a 404 from a wrong URL, a login redirect, or a proxy or CDN error page. In React Native this often means a release build is pointing at the wrong API base URL.

Invalid
<!DOCTYPE html>
<html><head><title>502 Bad Gateway</title>...

2. Unrecognized token '' (looks empty): a byte-order mark

The response or file starts with a UTF-8 BOM (U+FEFF), an invisible character that some Windows tools and servers prepend. It renders as nothing inside the quotes. Strip it before parsing.

3. Related messages from the same engine

An empty body gives "JSON Parse error: Unexpected EOF". Passing undefined or plain text gives "Unexpected identifier "undefined"" or "Unexpected identifier "Error"". Single quotes give "Single quotes (') are not allowed in JSON". The fixes below cover all of them: look at the raw text first.

How to fix it

Check the response before parsing it
const res = await fetch(url);
const text = await res.text();

if (!res.ok) {
  throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);
}
if (!res.headers.get("content-type")?.includes("application/json")) {
  throw new Error(`Expected JSON, got: ${text.slice(0, 200)}`);
}
const data = JSON.parse(text);
Strip a byte-order mark
const clean = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
const data = JSON.parse(clean);
React Native: log what the device actually received
// The simulator and a release build can hit different hosts.
console.log("API base:", API_BASE_URL);

const res = await fetch(`${API_BASE_URL}/users`);
console.log(res.status, res.headers.get("content-type"));
console.log((await res.clone().text()).slice(0, 300));

Frequently asked questions

Why does it work in Chrome but fail in Safari or my React Native app?

Usually it does not really work in Chrome either - Chrome just words the error differently. If the request genuinely succeeds in one place, compare what differs: cookies and sessions (the failing client may be logged out and getting the HTML login page), the base URL, or a CORS or proxy rule that only applies to one origin.

Is this a React Native bug?

No. The message comes from the JavaScript engine and means the text was not JSON. React Native apps on the Hermes engine word the error differently, but the cause and the fix - inspect the raw response - are the same.

How do I see the hidden character in an empty-looking quote?

Log the character code: console.log(text.charCodeAt(0)). 65279 (0xFEFF) is a byte-order mark. Our BOM guide covers where it comes from and how to stop servers and editors from adding it.

Last updated:

Related JSON errors

You might also need

Free and in-browser, like everything on JSON Console. Browse all tools →