Fix JSON.parse Error: Unexpected Token (All Solutions)
The SyntaxError: Unexpected token is one of the most common JavaScript errors. It appears in fetch calls, API responses, configuration files, and data pipelines. This guide covers every possible cause - with exact code fixes for each one.
What This Error Actually Means
When JavaScript's JSON.parse() function encounters a character it does not expect at a given position in the input string, it throws a SyntaxError. The message varies by browser and runtime, but they all mean the same thing: the string you passed to JSON.parse() is not valid JSON.
The error messages look like this across different environments:
Chrome/Edge: SyntaxError: Unexpected token u in JSON at position 0
Firefox: SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
Safari: SyntaxError: JSON Parse error: Unrecognized token '<'
Node.js: SyntaxError: Unexpected token < in JSON at position 0
The error message is trying to tell you what character was found where it was not expected. An angle bracket < at position 0 almost always means the server returned an HTML page instead of JSON. A u at position 0 means undefined was passed. An O at position 0 means the word Object is there - which happens when you accidentally stringify a JavaScript object reference rather than the object itself.
The 8 Most Common Causes and Their Fixes
Cause 1: Server Returned HTML Instead of JSON
This is far and away the most frequent cause. Your API endpoint returned a 404 page, a 500 error page, a login redirect, or a server error - all of which are HTML. Your client side code then blindly tries to parse that HTML as JSON and fails immediately on the < in <!DOCTYPE html>.
The fix is to always check the response status and Content-Type header before calling .json():
// WRONG - blindly parses whatever the server sends
const data = await fetch('/api/users').then(r => r.json());
// CORRECT - validate before parsing
async function fetchJson(url) {
const res = await fetch(url);
// Check HTTP status first
if (!res.ok) {
throw new Error(`HTTP error: ${res.status} ${res.statusText}`);
}
// Check Content-Type header
const contentType = res.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
const text = await res.text();
throw new Error(`Expected JSON but got ${contentType}. Body: ${text.slice(0, 200)}`);
}
return res.json();
}
Cause 2: Empty or Undefined Response Body
Calling JSON.parse(''), JSON.parse(null), or JSON.parse(undefined) all throw immediately. Empty responses are common from APIs that return 204 No Content, or from race conditions where a variable is accessed before being populated.
SyntaxError: Unexpected token u in JSON at position 0
// Safe parsing with a fallback
function safeParse(text, fallback = null) {
if (!text || text.trim() === '') return fallback;
try {
return JSON.parse(text);
} catch (e) {
console.error('JSON parse failed:', e.message, '\nInput:', text.slice(0, 100));
return fallback;
}
}
// For fetch responses that may be 204 No Content
const text = await res.text();
const data = text ? JSON.parse(text) : null;
Cause 3: BOM (Byte Order Mark) at Position 0
Files saved with a BOM (Byte Order Mark, \uFEFF) prepend an invisible character before the first {. The parser sees this invisible character at position 0 and fails. This is most common when JSON files are edited in Windows Notepad or some IDEs.
// Strip BOM before parsing
const cleanText = text.replace(/^\uFEFF/, '');
const data = JSON.parse(cleanText);
// Node.js file read with BOM stripping
const fs = require('fs');
const raw = fs.readFileSync('data.json', 'utf8').replace(/^\uFEFF/, '');
const data = JSON.parse(raw);
Cause 4: Trailing Commas
JavaScript objects and arrays allow trailing commas, but JSON does not. If you write JSON by hand or copy it from JavaScript source, this is a common mistake.
// INVALID JSON - trailing comma after last property
{
"name": "Alice",
"age": 30,
}
// VALID JSON
{
"name": "Alice",
"age": 30
}
If you need to write JSON with comments and trailing commas (for config files), use JSONC format and parse with a JSONC-aware parser, or strip them programmatically before passing to JSON.parse().
Cause 5: Single Quotes Instead of Double Quotes
JSON requires double quotes for all strings and keys. Single quotes are valid JavaScript but invalid JSON. This error often appears when people manually construct JSON strings or copy JavaScript object literals.
// INVALID JSON
{'name': 'Alice', 'age': 30}
// VALID JSON
{"name": "Alice", "age": 30}
Cause 6: Double-Encoded JSON (JSON Stringified Twice)
This happens when a value gets serialized to JSON twice: once by a backend framework, and then again explicitly by the developer. You end up with a JSON string whose value is another JSON string. Symptoms include seeing escaped quotes (\") everywhere and the response looking like a string rather than an object.
// The API returns a string that is itself JSON:
// "\"{\\"name\\":\\"Alice\\"}\"" - double encoded
// Detecting double encoding
const result = JSON.parse(response);
if (typeof result === 'string') {
// It was double-encoded - parse again
const data = JSON.parse(result);
}
// Or use a safe double-parse utility
function parseOnceOrTwice(input) {
const first = JSON.parse(input);
if (typeof first === 'string') {
try { return JSON.parse(first); } catch { return first; }
}
return first;
}
Cause 7: JSON with JavaScript Comments
Standard JSON does not support comments. If someone has written // comment or /* block comment */ in a JSON file, JSON.parse() will throw. This is very common in config files (like tsconfig.json) that use JSONC format.
// Strip single-line and block comments before parsing
function parseJsonWithComments(text) {
// Remove single-line comments
const noSingleLine = text.replace(/\/\/.*$/gm, '');
// Remove block comments
const noComments = noSingleLine.replace(/\/\*[\s\S]*?\*\//g, '');
return JSON.parse(noComments);
}
// Or use the 'comment-json' npm package for proper JSONC parsing
Cause 8: Unescaped Special Characters in Strings
JSON strings must escape certain characters: \" for quotes, \\ for backslashes, \n for newlines, \t for tabs. Raw newlines or backslashes inside JSON string values will cause parse failures.
// INVALID - raw newline in string value
{"message": "Hello
World"}
// VALID - escaped newline
{"message": "Hello\nWorld"}
// INVALID - unescaped backslash (common in Windows paths)
{"path": "C:\Users\Alice\file.txt"}
// VALID - escaped backslash
{"path": "C:\\Users\\Alice\\file.txt"}
Step-by-Step Debugging Process
When you encounter this error, follow these steps in order:
- Read the error message carefully. The character in "Unexpected token X" tells you exactly what the parser found.
<= HTML.u= undefined.'= single quotes.O= "[Object object]". - Log the raw string before parsing. Add
console.log(typeof text, JSON.stringify(text).slice(0, 200))before yourJSON.parse()call to see exactly what you are trying to parse. - Check the network tab. In browser DevTools, go to Network → find your API request → check the Response tab. Is it actually JSON? Is the status 200?
- Check Content-Type header. If the response
Content-Typeistext/htmlinstead ofapplication/json, the server is sending HTML. - Paste into a JSON validator. Copy the raw response body and paste it into our JSON Formatter - it will pinpoint the exact line and column of the error.
- Check for invisible characters. BOM characters are invisible. Use the hex view or our formatter's validation mode to detect them.
Validate Your JSON Instantly
Paste your JSON and get the exact error location with line and column numbers highlighted. Free, runs entirely in your browser - nothing is sent to any server.
Open JSON FormatterDefensive JSON Parsing Pattern
Here is a production-ready utility function that handles all the cases above:
/**
* Safely parse a JSON string with detailed error reporting.
* Handles: empty input, BOM, double-encoding, non-JSON responses.
*/
function safeJsonParse(input, options = {}) {
const { fallback = null, stripBom = true, allowDoubleEncoded = true } = options;
if (input === null || input === undefined || input === '') {
return fallback;
}
let text = String(input);
// Strip BOM
if (stripBom) {
text = text.replace(/^\uFEFF/, '');
}
// Check for obvious non-JSON (HTML page)
if (text.trimStart().startsWith('<')) {
console.warn('safeJsonParse: input appears to be HTML, not JSON');
return fallback;
}
try {
const result = JSON.parse(text);
// Handle double-encoded JSON
if (allowDoubleEncoded && typeof result === 'string') {
try { return JSON.parse(result); } catch { return result; }
}
return result;
} catch (e) {
console.error(`safeJsonParse failed: ${e.message}`);
console.error('Input preview:', text.slice(0, 200));
return fallback;
}
}
Python: json.loads() Equivalent Errors
The same errors occur in Python's json.loads(), just with different messages:
import json
# Python equivalent errors
try:
data = json.loads(text)
except json.JSONDecodeError as e:
print(f"Parse error at line {e.lineno}, col {e.colno}: {e.msg}")
print(f"Input preview: {text[:200]}")
# Safe parsing utility for Python
def safe_json_parse(text, fallback=None):
if not text or not text.strip():
return fallback
text = text.lstrip('\ufeff') # Strip BOM
try:
return json.loads(text)
except json.JSONDecodeError as e:
print(f"JSON parse error: {e}")
return fallback
FAQ
Why does the error say "at position 0" when my JSON looks correct?
Position 0 errors almost always mean the very first character is wrong. The most common causes are: (1) a BOM character \uFEFF that is invisible in most editors; (2) the string is undefined (which becomes the literal string "undefined"); (3) the string starts with an HTML tag because the server returned an error page. Log the raw input with JSON.stringify(text).slice(0, 50) to see invisible characters.
What is the difference between JSON.parse() and eval() for parsing JSON?
Never use eval() to parse JSON. eval() will execute any JavaScript, making it a critical security vulnerability if the input comes from an external source. JSON.parse() is strictly a data parser and will not execute code. It is also significantly faster for large inputs.
How do I handle APIs that sometimes return JSON and sometimes return HTML errors?
Always check response.ok and the Content-Type header before calling response.json(). For a robust approach, read the body as text first (response.text()), then attempt to parse it as JSON, and fall back to treating it as an error message if parsing fails.
Can JSON contain JavaScript comments?
No. Standard JSON (RFC 8259) does not support comments. If you need comments in configuration files, use JSONC (JSON with Comments), YAML, or TOML. The files tsconfig.json and .vscode/settings.json use JSONC - they look like JSON but need a JSONC-aware parser. Do not pass them to JSON.parse() directly if they contain comments.
Why does JSON.parse(JSON.stringify(obj)) sometimes fail?
It should not fail, but JSON.stringify() returns undefined (not the string "undefined") for values that cannot be represented in JSON: functions, undefined, Symbol, BigInt, and circular references. If the input object contains any of these, stringify will silently drop them or throw, and subsequent parse calls on a partial result may fail. Check for these types before stringifying.
Use our free tool here → JSON Formatter - paste any JSON to instantly validate it, see exact error locations, and fix syntax issues before they reach your application.
Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.