Unexpected token < in JSON at position 0

Unexpected token '<', "<html><bod"... is not valid JSON
(classic message: "Unexpected token < in JSON at position 0")

This error almost always means your code received an HTML page — typically an error page, login redirect, or 404 — but tried to parse it as JSON. The "<" character at position 0 is the start of an HTML tag. It is the most common error when working with REST APIs because most servers return an HTML error page for unknown routes or server failures, and JavaScript fetch() will happily give you that HTML body if you call response.json() without checking response.ok first.

Not checking response.ok before calling response.json()

A 404, 500, or 503 response usually has a Content-Type of text/html. If your code calls response.json() regardless of the status code, you will parse the HTML error body and see this error.

Invalid

const res = await fetch("/api/user/99")
const data = await res.json()  // 404 returns HTML — throws

Valid

const res = await fetch("/api/user/99")
if (!res.ok) {
  throw new Error(`HTTP ${res.status}: ${res.statusText}`)
}
const data = await res.json()

API base URL is wrong — hitting a CDN 404 or reverse proxy

A misconfigured proxy, a typo in the base URL, or deploying to a path where the API does not exist causes the server to return its default 404 HTML page. The fix is to verify the full URL in network developer tools.

Invalid

// Intended: https://api.example.com/v2/users
// Actual:   https://example.com/v2/users (no /api prefix)
// Server returns: <html>404 Not Found</html>

Valid

// Verify with: curl -I https://api.example.com/v2/users
// Ensure NEXT_PUBLIC_API_BASE_URL is set correctly

Session expired — server redirects to login page

Many server-side rendering frameworks redirect expired sessions to a login page that returns HTML. If your client code does not detect the 302 or 401 and re-authenticate, subsequent JSON requests will receive the HTML login page.

Invalid

// Server returns 302 → /login (HTML page)
// fetch follows redirect and returns HTML body

Valid

if (res.status === 401) {
  // Redirect to login or refresh token
  window.location.href = "/login"
  return
}

Local development proxy not configured

In Next.js and Create React App, a missing or misconfigured proxy means /api/... requests go to the static dev server, which returns its own HTML 404 page instead of forwarding to the backend.

Invalid

// next.config.mjs missing rewrites for /api routes
// Dev server returns: <!DOCTYPE html>...

Valid

// next.config.mjs
rewrites: async () => [
  { source: "/api/:path*", destination: "http://localhost:4000/:path*" }
]

Fix this error in seconds

Paste your JSON in the free validator to find the exact line and column where the error occurs.

Open JSON Validator →

Frequently Asked Questions

How can I see what the server actually returned?

Use the Network tab in browser DevTools or run: const text = await response.text(); console.log(text); This logs the raw response body before you try to parse it, so you can see whether it is HTML, a plain-text error, or malformed JSON.

Does this error mean my JSON is malformed?

No. The angle bracket error almost always means you are not receiving JSON at all — the server sent an HTML page, XML document, or plain-text error. Your JSON is likely fine; the problem is upstream in the HTTP layer. Fix the request URL, add response.ok checks, and handle authentication redirects.

More JSON errors