SyntaxError: Unexpected token < in JSON at position 0
Your code called JSON.parse() (often via res.json()) on a response that starts with "<" — almost always an HTML page: a 404, a login/redirect page, or a server error returned where you expected JSON. The parser dies on the very first character. The fix is in the request/response, not the parser.
What the error means
JSON.parse() reads the response as text and expects it to begin with valid JSON ({, [, a number, a string, etc.). It got < instead — position 0 — so it throws immediately. A leading < almost always means the body is HTML, not JSON.
The usual cause
The endpoint returned an HTML page instead of JSON. Common triggers: a wrong URL that hits a 404 page, an unauthenticated request redirected to a login page, a proxy/error page (502/500), or an API that returns HTML on error. Your fetch() succeeded at the network level, so res.json() still tried to parse the HTML body.
Fix it
1. Look at the raw body first. Replace await res.json() with const text = await res.text(); console.log(text); — you will see the HTML and the real error (404 page, stack trace, login form).
2. Check status before parsing. if(!res.ok) throw new Error(res.status); catches 404/500 before you parse.
3. Verify the URL and Content-Type. Confirm the endpoint is correct and returns application/json. Fix the URL, auth header, or server route so it actually returns JSON.
FAQ
Why position 0 and the token '<'?
The response body starts with '<' — the first character of HTML like <!DOCTYPE html> or <html>. JSON.parse fails on the first character, which is position 0.
My fetch didn't error though — why?
fetch() only rejects on network failure. A 404 or 500 with an HTML body is still a 'successful' response object; the failure happens later when you call res.json().
How do I stop it happening again?
Always check res.ok (or res.status) and the Content-Type header before parsing, and log res.text() when parsing fails so you can see what the server actually sent.