Developer Guides

Unexpected Token in JSON at Position 0: Causes and Fixes

Fix Unexpected token errors in JSON by checking the raw response, HTTP status, content type, quotes, commas, empty bodies, and hidden characters.

ONLINTools Editorial Team Updated Aug 2, 2026 11 min read Code example and standards review
response.jsondiagnostic

const raw = "<!doctype html>";

JSON.parse(raw);

Unexpected token '<' in JSON at position 0
01 · inspect raw body02 · check status03 · verify Content-Type

The error starts with the input, not the parser

“Unexpected token in JSON at position 0” means the first character of the value is not valid where a JSON value should begin. The fastest fix is to inspect the exact raw string passed to JSON.parse(). Do not retry the same parse or immediately replace characters.

In API code, the input is often not JSON at all. A leading < usually means an HTML error, login, or proxy page was returned. A leading u commonly means undefined reached the parser. An empty or truncated response more often produces an “Unexpected end of JSON input” message.

Use the unexpected character as a diagnostic clue

JavaScript engines vary in their exact error wording, but the reported character is still useful. Check it against the raw input before looking for a missing bracket deeper in the payload.

Message clueLikely inputFirst check
Unexpected token < at position 0HTML or XML responseStatus, redirects, and Content-Type
Unexpected token u at position 0undefined converted to textThe value before JSON.parse()
Unexpected token o at position 1An object converted to [object Object]Whether the value is already parsed
Unexpected token ' at position 0 or 1Single-quoted string or property nameReplace JSON syntax at the source
Unexpected token } or ]Trailing comma or missing valueThe item immediately before the token
Unexpected end of JSON inputEmpty or incomplete bodyBody length and upstream connection

A safe workflow for debugging JSON responses

Preserve the response as text for one diagnostic pass. That lets you inspect the HTTP result and the raw body together. A response can have a valid network connection while still carrying an HTML error page, a plain-text message, or no content.

Inspect the response before parsingJavaScript
const response = await fetch(url);
const raw = await response.text();

console.log({
  status: response.status,
  contentType: response.headers.get("content-type"),
  preview: raw.slice(0, 120),
});

if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${raw.slice(0, 120)}`);
}

if (!raw.trim()) {
  return null; // Decide explicitly how your app handles an empty body.
}

const data = JSON.parse(raw);
  1. Check the status. A 401, 403, 404, 429, or 500 response often has a non-JSON body even when the successful response is documented as JSON.
  2. Check Content-Type. For a JSON API, expect a media type such as application/json. A header is a useful signal, but the body must still contain valid JSON.
  3. Print a short escaped preview. UsingJSON.stringify(raw.slice(0, 120)) makes newlines, tabs, and some hidden characters visible.
  4. Validate the captured body. If it really is JSON, a validator can identify the structural error without changing the data.
  5. Fix the producer. Correct the API, template, serializer, or stored value that generated the invalid response.

Fix the JSON grammar, not just the visible character

RFC 8259 defines JSON as a serialized value. Objects contain double-quoted string names, a colon between each name and value, and a comma only between members. Arrays likewise use commas only between values. JavaScript object-literal conveniences do not automatically belong to JSON.

ProblemInvalidValid JSON
Single quotes{'name': 'Ada'}{"name": "Ada"}
Unquoted name{name: "Ada"}{"name": "Ada"}
Trailing comma{"active": true,}{"active": true}
JavaScript value{"score": undefined}{"score": null}
Uppercase literal{"active": True}{"active": true}
Unescaped newline{"note": "line 1 line 2"}{"note": "line 1\nline 2"}

Values such as NaN, Infinity, functions, comments, and undefined are not part of JSON grammar. A top-level string, number, boolean, or null is valid under RFC 8259, although some older systems accept only an object or array.

The same parser error can enter through different boundaries

The parser message describes the text, not where that text came from. Locating the boundary that introduced it is often more useful than staring at the final JSON.parse() call.

Fetch and response.json()

response.json() reads the response stream and parses it. If parsing fails, temporarily read the same request with response.text() so you can see whether the server sent JSON, HTML, or an empty body. Also check response.ok; Fetch does not reject its promise merely because the HTTP status is 404 or 500.

Axios and other HTTP clients

Some clients automatically deserialize JSON responses. Inspect the type of response.databefore parsing it again. If it is already an object or array, use it directly. If it is a string, inspect its first characters and the response headers before deciding how to parse it.

Environment variables and optional values

A missing environment variable or object property evaluates to undefined. Passing that value to JSON.parse() commonly creates the u at position 0 error. Validate required configuration at application startup and fail with a message that names the missing key.

localStorage and cached data

Browser storage may contain an older format, a partially written value, or plain text from a previous release. Version stored records, handle a missing key separately, and remove or migrate entries that no longer match the expected JSON shape. Successful parsing still does not prove the object has the fields your current code requires.

What does “position 0” actually point to?

Position 0 is the beginning of the string supplied to the parser. Higher position numbers point near the character where the parser could no longer continue, not necessarily where the original mistake began. A missing quote or bracket earlier in the text can make a later character look unexpected.

Error formats are engine-specific. One browser may report a zero-based position while another reports a line and column. Use the number to create a small context window, then inspect the structure before and after it.

Return an escaped preview with the parser messageJavaScript
function parseJsonWithContext(raw) {
  try {
    return { ok: true, value: JSON.parse(raw) };
  } catch (error) {
    return {
      ok: false,
      error: error instanceof Error ? error.message : String(error),
      preview: JSON.stringify(raw.slice(0, 120)),
    };
  }
}

For long payloads, calculate the line and column from the characters before the reported position or paste the captured text into the ONLINTools JSON Formatter. Formatting valid sections makes mismatched brackets and missing separators easier to see.

Prevent parsing failures at the boundary

  • Serialize with a JSON library. Generate payloads withJSON.stringify() or the equivalent serializer in your language instead of concatenating strings by hand.
  • Return consistent error envelopes. If an API promises JSON, its error responses should also use JSON and the appropriate HTTP status.
  • Test non-success paths. Authentication expiry, rate limits, reverse-proxy failures, and missing routes are frequent sources of HTML bodies in JSON clients.
  • Validate at system boundaries. Check untrusted API, file, queue, and local-storage data before the rest of the application depends on its shape.
  • Log safely. A short preview helps debugging, but redact access tokens, personal data, and secrets before storing request or response bodies.

Common questions about unexpected JSON tokens

Why does the error happen only in production?

Production adds authentication redirects, CDNs, proxies, rate limits, and custom error pages. Compare status, headers, redirect history, and the first part of the production response with the working development response.

Should I use response.json() or JSON.parse()?

Use response.json() when you expect a normal JSON response and want the Fetch API to read and parse it. During diagnosis, response.text() followed by JSON.parse(raw)exposes the exact body that failed.

Can a byte order mark cause position 0?

It can in parsers that reject a leading byte order mark. RFC 8259 says networked JSON must not add a BOM, though parsers may choose to ignore one for interoperability. Fix the file or producer encoding instead of assuming every consumer will tolerate it.

Is the ONLINTools JSON Formatter private?

Formatting and validation run locally in the browser. Even so, remove production credentials and personal information from debugging samples whenever practical.

Sources and further reading

  1. 1.RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format RFC Editor
  2. 2.JSON.parse(): Syntax, exceptions, and illegal JSON examples MDN Web Docs
  3. 3.SyntaxError: JSON.parse: bad parsing MDN Web Docs
  4. 4.Using the Fetch API: reading response bodies MDN Web Docs