Unexpected token '<', "<!DOCTYPE "... is not valid JSON
SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
Where it stops
<!DOCTYPE html>
<html>
<head><title>404 Not Found</title></head>What it means
Your code asked for JSON and got an HTML page instead. <!DOCTYPE html> is how an HTML page starts, and no JSON document can start with <. Nothing is wrong with your parsing code. The problem is the response, so the fix is to find out why the server sent a page.
Last verified on Chromium 145, Firefox 146 and WebKit 26.
The validator opens with the error marked. Examples travel in the URL fragment, which browsers never send to a server.
Express Lint formats broken JSON so you can find the error, and marks the line where it stops. It changes only spaces and line breaks outside strings. Strings in either quote stay as written. If the quotes do not pair up, it leaves your text as it is. Ctrl+Z undoes it. See how it works.
- Usually
- The URL is wrong, so you got the 404 pageA single-page app answered with index.htmlThe request needed a loginThe server failed and sent its error page
- Fix
- Check the response before you parse it.
The fix
Check the response before you parse it. response.ok is false for every error status, and the Content-Type header should include application/json. When either check fails, read the body with response.text() and log it, so you can see the page that came back. Then fix whatever it points to: the URL, the proxy or the login.
<!DOCTYPE html>
<html>
<head><title>404 Not Found</title></head>
<body>Not Found</body>
</html>5 lines change.
{
"id": 42,
"name": "Ada"
}How the message appears
Chrome and Node first, then Firefox, then Safari. The two lines after those are what response.json() says in Chrome and in Safari. Safari does not mention JSON at all there. Every one of these is the same failure: the first character was <.
- SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSONChrome and Node
- SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON dataFirefox
- SyntaxError: JSON Parse error: Unrecognized token '<'Safari
- SyntaxError: Failed to execute 'json' on 'Response': Unexpected token '<', "<!DOCTYPE "... is not valid JSON
- SyntaxError: The string did not match the expected pattern.
- SyntaxError: Unexpected token < in JSON at position 0Older V8 wording
What causes it
The URL is wrong, so you got the 404 page
A typo in the path, a missing
/apiprefix, or a trailing slash the server treats differently. Most frameworks answer an unknown URL with an HTML error page, not a JSON one. Open the exact URL from the Network tab in a new browser tab and you will see the page your code tried to parse.A single-page app answered with index.html
Dev servers like Vite, and static hosts with a catch-all rewrite, answer every unknown path with
index.htmlso client-side routing works. Ask for/api/userswhen no API is running there and you get the app shell with a200. The status looks fine and the body is HTML.The request needed a login
An expired session or a missing token often gets a redirect to the login page rather than a
401.fetchfollows redirects by default, so your code receives the login form.The server failed and sent its error page
A
500from your framework, a502from a proxy or a503from a load balancer usually comes with an HTML body.fetchdoes not reject on an error status, soresponse.json()runs on that page and throws this instead of telling you about the500.
Worth knowing
NoteA body can only be read once. If you want the text for your logs when parsing fails, call response.text() first and then JSON.parse(text) inside a try, rather than calling response.json().
NoteSafari words this differently. JSON.parse says Unrecognized token '<', and response.json() says The string did not match the expected pattern., which gives no hint that JSON is involved. It is the same problem.
NoteThe same failure in other languages: Python says Expecting value: line 1 column 1 (char 0) and Go says invalid character '<' looking for beginning of value. Both are in this reference: the Python page and the Go page.