Home / Why Your JSON.parse() Keeps Failing: The “Unexpected Token” Error Explained

Why Your JSON.parse() Keeps Failing: The “Unexpected Token” Error Explained

JSON Parse Unexpected Token Error Why Your JSON.parse() Keeps Failing: The "Unexpected Token" Error Explained
Json Beautifier May 31, 2026 · 8 min read

You’ve written the code a hundred times. const data = JSON.parse(response). It always works.

Except today. Today, your console is screaming:

SyntaxError: Unexpected token < in JSON at position 0

And you’re staring at the screen wondering what changed.

Here’s the thing – JSON.parse() isn’t actually broken, and your JSON probably isn’t broken in the way you think. The “Unexpected Token” error is almost always one of seven very specific situations. Once you know which one is biting you, the fix takes about ten seconds.

Let’s walk through all seven, with the actual error messages you’ll see and exactly what to do.

First: What This Error Message Actually Says

The error follows a predictable format:

SyntaxError: Unexpected token X in JSON at position N

  • X is the specific character the parser choked on
  • N is the position in the string where that character appears

Read this literally. If it says Unexpected token <, the parser found a < symbol where it expected JSON. That’s a real clue — it usually means the server returned HTML instead of JSON. Each character has a different story, and we’ll cover the common ones below.

Cause #1: The Server Returned HTML, Not JSON

This is the #1 reason for JSON.parse() failures in production. The error usually looks like:

SyntaxError: Unexpected token < in JSON at position 0

That < is the opening of <!DOCTYPE html> or <html>. Your API returned an HTML error page – usually a 404, 500, or a login redirect – and your code tried to parse it as JSON.

How to debug:

Before parsing, log the raw response:

javascript

const response = await fetch('/api/users');
const text = await response.text();
console.log('Raw response:', text);
console.log('Status:', response.status);

If you see HTML in the console, your API isn’t returning JSON. The cause might be:

  • A 404 (wrong URL)
  • A 500 (server crashed)
  • A redirect to a login page (auth expired)
  • A misconfigured proxy or CDN returning its own error page

The fix: Always check response.ok or the status code before parsing:

javascript

const response = await fetch('/api/users');
if (!response.ok) {
  throw new Error(`API failed with status ${response.status}`);
}
const data = await response.json();

Cause #2: Empty Response – “Unexpected End of JSON Input”

The error message here is slightly different:

SyntaxError: Unexpected end of JSON input

This means JSON.parse() reached the end of the string before finding complete, valid JSON. The two most common reasons:

1. The server returned an empty body. Some APIs return 204 No Content on success, or an empty 200 on certain operations. Your code then tries to parse “”, which fails immediately.

2. The response got truncated mid-transfer. Network hiccup, proxy timeout, or a server that closed the connection early.

The fix:

javascript

const text = await response.text();
if (!text) {
  return null; // or {} or whatever default makes sense
}
const data = JSON.parse(text);

If truncation is the issue, log response.headers.get(‘content-length’) and compare it to text.length. If they don’t match, something between the server and your code is chopping the response.

Cause #3: You’re Parsing Something That’s Already an Object

This one is sneaky because the code looks right.

javascript

const response = await axios.get('/api/users');
const data = JSON.parse(response); // ❌ Will fail

Axios (and many other HTTP clients) automatically parse JSON responses for you. So response is already a JavaScript object – and JSON.parse() expects a string. When it tries to parse [object Object], it dies.

The error usually looks like:

SyntaxError: Unexpected token o in JSON at position 1

That o is the second character of [object Object].

The fix: Don’t double-parse.

javascript

const response = await axios.get('/api/users');
const data = response.data; // Already parsed by axios

The general rule: if your HTTP library exposes a response. data or returns the parsed body directly; never call JSON.parse() on it.

Cause #4: JSONP Response Wrapped in a Function Call

If you’re working with older APIs (or some legacy financial/government endpoints), you might get a response that looks like this:

javascript

callback({"name": "Alice", "age": 30});

This is JSONP – a workaround from before CORS existed. The data is JSON wrapped inside a function call.

JSON.parse() chokes on it because of the leading callback(:

SyntaxError: Unexpected token c in JSON at position 0

The fix: Either configure your client to actually use JSONP, or strip the wrapper manually:

javascript

const text = await response.text();
const jsonPart = text.replace(/^[^(]+\(/, '').replace(/\)\s*;?\s*$/, '');
const data = JSON.parse(jsonPart);

That regex removes everything up to and including the opening (, and the closing ) and optional semicolon. Not elegant – but it works for nearly any JSONP response in the wild.

Cause #5: The JSON Itself Is Malformed

Sometimes the server does return JSON. It’s just broken JSON.

Trailing commas, single quotes, unescaped characters, missing brackets – any of these will trip up JSON.parse(). The error message varies depending on what’s wrong:

  • Unexpected token } in JSON at position 47 – trailing comma before the }
  • Unexpected token ‘ in JSON at position 5 – single quotes used instead of double
  • Unexpected token \n in JSON at position 23 – unescaped newline inside a string

The fix: Paste the raw response into a JSON validator. It will highlight the exact issue. Then either fix the source that’s generating the JSON, or – if you don’t control the source – clean it up before parsing.

For a full breakdown of malformed-JSON patterns, we covered the 10 most common JSON errors in a separate post. Worth bookmarking.

Cause #6: BOM or Hidden Characters at the Start

Some servers (especially older PHP, classic ASP, or anything writing UTF-8 files in Windows) prepend a Byte Order Mark to the response. It’s an invisible character that looks like nothing in your console but absolutely breaks JSON.parse().

The error is usually:

SyntaxError: Unexpected token  in JSON at position 0

That blank space after “token” is actually the BOM character – invisible, but there.

The fix:

javascript

const text = await response.text();
const clean = text.replace(/^\uFEFF/, ''); // Strip BOM if present
const data = JSON.parse(clean);

The same trick saves you from invisible whitespace pasted from PDFs, Slack messages, or Word documents. If you’re parsing JSON that came from a copy-paste, always normalize it first.

Cause #7: Double-Stringified JSON

This one drives developers crazy because the JSON looks valid:

javascript

const response = "\"{\\\"name\\\":\\\"Alice\\\"}\"";
const data = JSON.parse(response);
console.log(data); // "{\"name\":\"Alice\"}" — still a string!

What happened? The data was stringified twice somewhere upstream. Maybe someone did JSON.stringify(JSON.stringify(obj)). Maybe a queue serializer wrapped an already-JSON payload. Either way, one parse gets you a string, not an object.

The fix: Parse twice.

javascript

const data = JSON.parse(JSON.parse(response));

But really, the root fix is to find where the double-stringification is happening upstream and remove it. Double-parsing is a workaround, not a solution.

How to spot this: if JSON.parse() succeeds but typeof result === ‘string’, you’ve got double-stringified JSON.

The 30-Second Debug Checklist

When the error hits and you don’t know where to start, run through this in order:

  1. Log the raw response before parsing. Is it actually JSON? Or is it HTML, empty, or wrapped in something?
  2. Check the response status. A 4xx or 5xx almost always means the body isn’t JSON.
  3. Check what you’re parsing. Is it already an object? If yes, you don’t need JSON.parse().
  4. Count the characters. If the error says “position 23”, look at character 23. Literally count to it.
  5. Run it through a validator. Paste the raw response into a JSON validator – it’ll point at the exact broken character.
  6. Check for invisible characters. If position 0 looks fine to your eyes, it’s probably a BOM or hidden whitespace.

In our experience, causes #1 (HTML response), #3 (already parsed), and #5 (malformed JSON) cover about 80% of real-world JSON.parse() failures. Start there.

A Better Pattern: Wrap Your Parsing

Once you’ve debugged this enough times, you stop wanting to write the same try/catch over and over. Build a safe parser once and reuse it:

javascript

function safeJsonParse(text, fallback = null) {
  if (!text || typeof text !== 'string') return fallback;
  // Strip BOM if present
  const clean = text.replace(/^\uFEFF/, '').trim();
  if (!clean) return fallback;
  try {
    return JSON.parse(clean);
  } catch (err) {
    console.warn('JSON parse failed:', err.message);
    console.warn('First 100 chars:', clean.substring(0, 100));
    return fallback;
  }
}

This handles empty strings, BOMs, and bad inputs gracefully – and logs enough context that future-you can actually debug what went wrong.

The Honest Truth

JSON.parse() is one of the most reliable functions in JavaScript. When it throws, it’s almost never the parser’s fault – it’s the data you’re feeding it.

The fastest path to fixing any “Unexpected token” error is to look at the raw input first, before you touch the code. Nine times out of ten, the answer is staring back at you the moment you log the response.

And if you want to stop ever guessing at JSON errors again, json-beautifier.org runs entirely in your browser – paste the raw API response, see exactly what’s wrong, fix it, and move on. If you’re new to the world of JSON tools and not sure which one does what, we broke down the differences between beautifiers, validators, formatters, and minifiers in another post.

Now go fix that error. You’ve got this.

J

Json beautifier

Writer

Related posts