DevKitLab
JSON
2026-11-09
•
8 min read
•
Fact-Checked & RFC Verified

How to Fix SyntaxError: Unexpected token in JSON at position 0 (JavaScript & Fetch API)

Diagnose and fix the most common JSON.parse() and Fetch API runtime errors in JavaScript, TypeScript, and Node.js with defensive parsing patterns.

Sponsored Developer RecommendationAdSense Compliant Slot
JSON Developer Architecture Reference100% Client-Side Privacy

Comprehensive engineering standards, security safeguards, and client-side processing insights. Bookmark DevKitLab for rapid, zero-login daily engineering workflows.

When working with JSON.parse() or calling await response.json() in JavaScript, TypeScript, or Node.js, few errors appear more frequently than:

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

or in older V8 runtimes:

text
SyntaxError: Unexpected token < in JSON at position 0

This guide explains what character position 0 means, the five real-world causes of this exception, and how to write defensive HTTP response handlers that never crash in production.


Quick Answer

Why does Unexpected token < in JSON at position 0 happen?

Character position 0 is the very first character of the string passed to JSON.parse(). When that character is <, your code expected a JSON payload (starting with { or [), but the server actually returned an HTML document—most commonly an HTML 404 Not Found page, a 502 Bad Gateway error page from Nginx/Cloudflare, or an authentication login redirect.


Cause 1: Calling response.json() on an HTML Error Page (404 / 500 / 502)

The browser fetch() API does not throw an exception on HTTP 404 or 500 status codes. If you call response.json() without checking response.ok and the Content-Type header first, fetch attempts to parse the server's HTML error page as JSON:

javascript
// ❌ Fragile: Crashes if the endpoint returns an HTML 404 or 502 page
const res = await fetch("/api/v1/users");
const data = await res.json(); // Throws SyntaxError at position 0!

The Production Fix: Verify response.ok and Content-Type

Always verify both the HTTP status code and the content-type header before invoking .json():

javascript
// ✅ Defensive: Inspects status and Content-Type before JSON parsing
async function fetchJsonSafely(url) {
  const response = await fetch(url, {
    headers: { Accept: "application/json" }
  });

  const contentType = response.headers.get("content-type") || "";

  if (!response.ok) {
    const errorPreview = (await response.text()).slice(0, 200);
    throw new Error(`HTTP ${response.status} from ${url}: ${errorPreview}`);
  }

  if (!contentType.includes("application/json")) {
    const rawBody = (await response.text()).slice(0, 200);
    throw new TypeError(`Expected application/json but received ${contentType}: ${rawBody}`);
  }

  return response.json();
}

Cause 2: SyntaxError: Unexpected end of JSON input (Empty 204 Response)

When a REST API responds with 204 No Content (common on DELETE or PUT endpoints), the response body is completely empty (""). Passing an empty string to JSON.parse("") throws Unexpected end of JSON input:

javascript
// ✅ Safely handle empty 204 No Content responses
const rawText = await response.text();
const data = rawText.trim() ? JSON.parse(rawText) : null;

Cause 3: Double JSON.parse() on an Already Parsed Object

Libraries such as Axios (axios.get()) automatically parse JSON responses into a JavaScript object on response.data. If you pass response.data into JSON.parse(response.data), JavaScript coerces the object to the string "[object Object]", which fails at position 1 (Unexpected token o in JSON at position 1).


Cause 4: UTF-8 Byte Order Mark (BOM) or Markdown Code Fences

When reading JSON files saved by Windows Notepad or LLM completions wrapped in ``json fences, invisible leading characters or backticks at position 0` break strict RFC 8259 parsers. Paste the raw payload into the DevKitLab JSON Validator or JSON Formatter to pinpoint the exact line and column and preview a non-silent RFC 8259 repair.

Sponsored Developer RecommendationAdSense Compliant Slot
DevKitLab Interactive Utilities100% Client-Side Privacy

Explore our 44+ instant, client-side developer formatters and decoders without accounts or subscriptions. Bookmark DevKitLab for rapid, zero-login daily engineering workflows.

Interactive Tools for This Guide

Frequently Asked Questions

What does 'Unexpected token o in JSON at position 1' mean?

It means you passed a JavaScript Object directly into JSON.parse() instead of a string. JavaScript converts the object to '[object Object]', starts reading '[' as an array at position 0, and fails on the letter 'o' at position 1.

How do I find the exact line and column of a JSON SyntaxError?

Paste your payload into DevKitLab's client-side JSON Validator (/tools/json-validator). It translates the parser's character offset into a 1-based Line and Column number and highlights the offending line.

More Developer Guides & Tutorials