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.
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.
<!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
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);const clean = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
const data = JSON.parse(clean);// 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 →