Skip to content

/errors/json/unexpected-token-not-valid-json

SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated 2026-10-20
tool
JSON
exit code
1
versions
Node.js 20+
os
any

JSON.parse (and response.json()) throws SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON when the text it receives is HTML instead of JSON. The < is the first character of <!DOCTYPE html>. The request reached a web page (an error page, a login page, or your dev server's index.html), not the API you expected.

Reproduce the error

bash
node -e 'JSON.parse("<!DOCTYPE html><html><body>Not Found</body></html>")' 2>&1 | grep SyntaxError; exit "${PIPESTATUS[0]}"

Output · exit 1

SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
bash
node -e '
for (const text of ["", "{\"a\": 1,}", "{a: 1}"]) {
  try { JSON.parse(text); } catch (e) { console.log(JSON.stringify(text).padEnd(14), "->", e.message); }
}'

Output

""             -> Unexpected end of JSON input
"{\"a\": 1,}"  -> Expected double-quoted property name in JSON at position 8 (line 1 column 9)
"{a: 1}"       -> Expected property name or '}' in JSON at position 1 (line 1 column 2)

Fix: check the response before parsing

js
const res = await fetch('/api/items');
const type = res.headers.get('content-type') ?? '';
if (!res.ok || !type.includes('application/json')) {
  const body = await res.text();
  throw new Error(`Expected JSON, got ${res.status} ${type}: ${body.slice(0, 80)}`);
}
const data = await res.json();

The error then tells you the real status code and shows the start of the HTML, which usually names the problem (404, 401, 502).

Sources