Quick answer

The server sent back HTML (usually starting with <!DOCTYPE html> or <html>) and your code tried to parse it as JSON. The parser fails on the very first character, which is why the position is 0. The JSON is not broken. The response is not JSON at all, so look at what the server actually returned.

The same error in different browsers

Most common reasons

How to find the real problem

Check the status code and content type, and print the start of the response before parsing:

const res = await fetch("/api/users");
const type = res.headers.get("content-type") || "";

if (!res.ok || !type.includes("application/json")) {
  const text = await res.text();
  console.log(res.status, type);
  console.log(text.slice(0, 200));   // you will see the HTML here
  throw new Error("Expected JSON but got " + type);
}

const data = await res.json();

You can also open the request in the browser Network tab and look at the Response tab. If it shows a web page, the problem is on the server or in the URL, not in your parsing code.

The same problem in Python

import requests

r = requests.get(url)
print(r.status_code, r.headers.get("content-type"))
print(r.text[:200])
data = r.json()   # fails if the body is HTML

The Python message is usually Expecting value: line 1 column 1 (char 0).

If the response really is JSON

Sometimes the text starts with a hidden character or extra text before the JSON. Paste it into the JSON Formatter to see where it breaks, or use the JSON Repair tool.

Have broken JSON right now?

Paste it into the free JSON Repair tool. It runs in your browser, so nothing is uploaded.

Open JSON Repair tool

FAQ

Why does the error point to position 0?

HTML starts with a less-than sign, and that is not a valid first character for JSON, so the parser fails immediately.

Why does it work locally but not in production?

Often the API base URL or proxy settings differ, so in production the request reaches a different server that returns an HTML page.

Is this a bug in JSON.parse?

No. JSON.parse is doing its job. The input is not JSON.

Related guides