Fixing JSON Parse Errors -- A Developer Debugging Guide
Last week, a production API started failing with Unexpected token < in JSON at position 0. The error message told me nothing useful. After two hours of debugging, I discovered our authentication middleware was redirecting to a login page instead of returning JSON.
If you have ever stared at a cryptic JSON parse error wondering what went wrong, this guide is for you. I will walk through the most common causes, show you exactly how I debug them, and share the workflow that saves me hours every week.
Why JSON Is So Strict
When you call JSON.parse(), the JavaScript engine runs a strict parser over your string. Unlike JavaScript object literals, JSON has zero tolerance:
- No trailing commas (ever)
- No comments (not even single-line)
- Double quotes only (single quotes are invalid)
- No undefined or functions
- Proper escaping required for special characters
The moment the parser encounters something unexpected, it throws a SyntaxError and stops. No partial parsing. No "best guess" interpretation. Just an error with a position number that rarely helps.
This strictness is intentional. It ensures JSON can be parsed consistently across all languages and platforms. But it also means tiny mistakes break everything.
Error #1: Unexpected Token o -- The Double-Parsing Trap
The Symptom
SyntaxError: Unexpected token o in JSON at position 1
This is one of the most misleading error messages in JavaScript. That "o" is not a typo -- it is the second character of [object Object].
What Actually Happened
You are passing an already-parsed JavaScript object to JSON.parse().
I see this constantly in code reviews:
// WRONG: obj is already a JavaScript object
const obj = { name: "John", age: 30 };
JSON.parse(obj); // SyntaxError: Unexpected token o
// RIGHT: Pass a JSON string
const jsonString = "{\"name\": \"John\", \"age\": 30}";
const parsed = JSON.parse(jsonString); // Works!
Why This Keeps Happening
Two common scenarios:
1. Double-parsing API responses
Your HTTP client (axios, fetch with auto-parsing) already parsed the JSON, but you call JSON.parse() again:
// axios automatically parses JSON responses
const response = await axios.get("/api/user");
const user = JSON.parse(response.data); // ERROR! response.data is already an object
// Just use it directly
const user = response.data; // Correct
2. Confusing JSON.stringify output
const obj = { key: "value" };
const str = JSON.stringify(obj);
JSON.parse(obj); // ERROR - obj is not a string
JSON.parse(str); // Works - str is a JSON string
My Fix
I always check the type before parsing:
function safeParse(input) {
if (typeof input === "string") {
try {
return JSON.parse(input);
} catch (err) {
console.error("Failed to parse JSON:", err.message);
return null;
}
}
// Already an object, return as-is
return input;
}
This simple wrapper has saved me countless hours of debugging.
Error #2: Unterminated String -- The Missing Quote
The Symptom
SyntaxError: Unexpected end of JSON input
What Actually Happened
A string literal is missing its closing quote. The parser keeps reading, expecting more content, but hits the end of the input instead.
{
"name": "John,
"age": 30
}
See the missing closing quote after "John"? That is all it takes to break everything.
Where This Comes From
Manual editing mistakes
You are hand-editing a large JSON config file (we have all been there) and accidentally delete a quote. In a 500-line file, finding that missing quote manually is painful.
String concatenation bugs
// Building JSON manually (please do not do this)
const name = "OBrien";
const json = "{\"name\": \"" + name + "\"}"; // Breaks with apostrophes
Copy-paste errors
You copy JSON from documentation or an email and miss the last character. It happens more often than you think.
How I Fix It
First, I paste the JSON into our JSON Validator. It instantly highlights the exact line where the parser expected a closing quote. For large files, this is infinitely faster than scanning line by line.
But the real fix? Stop building JSON strings manually:
// BAD: Manual string building
const json = "{\"name\": \"" + name + "\", \"age\": " + age + "}";
// GOOD: Let JavaScript handle escaping
const json = JSON.stringify({ name, age });
JSON.stringify() handles all the escaping, quoting, and formatting for you. It never produces invalid JSON.
Error #3: Unexpected Token Less Than -- When HTML Sneaks In
The Symptom
SyntaxError: Unexpected token < in JSON at position 0
What Actually Happened
Your code expects JSON, but the server returned HTML. That < at position 0? It is the opening < of <!DOCTYPE html> or <html>.
This is arguably the most frustrating JSON error because the problem is not in your JSON at all.
Common Causes
From my experience, these are the usual suspects:
1. Authentication failures
// User session expired, server returns login page
fetch("/api/protected-data")
.then(res => res.json()) // ERROR: Tries to parse HTML as JSON
2. Wrong URL or 404 errors
// Typo in endpoint URL
fetch("/api/usrs") // Returns 404 HTML page
.then(res => res.json()) // ERROR
3. Server misconfiguration
Your reverse proxy (nginx, Apache) is returning an error page instead of forwarding to your API backend.
4. Cloudflare or CDN protection
Rate limiting or bot protection pages are HTML, not JSON. If you hit rate limits, you get an HTML challenge page.
My Debugging Process
Step 1: Check the raw response
Never assume the response is JSON. Inspect it first:
const response = await fetch("/api/data");
const text = await response.text();
console.log("Status:", response.status);
console.log("Content-Type:", response.headers.get("content-type"));
console.log("Response preview:", text.substring(0, 200));
// Now try to parse
try {
const data = JSON.parse(text);
console.log("Success:", data);
} catch (err) {
console.error("Parse failed:", err.message);
console.log("Raw response:", text);
}
Nine times out of ten, you will immediately see the problem. The raw response will show you HTML markup, an error page, or a redirect notice.
Step 2: Check the Network tab
Open browser DevTools, go to the Network tab, find the request, and check:
- Status code (should be 200, not 401/403/404/500)
- Response headers (
Content-Type: application/json) - Response body (is it HTML or JSON?)
Step 3: Handle errors gracefully
I now wrap all JSON parsing with proper error handling:
async function fetchJSON(url) {
const response = await fetch(url);
// Check status
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
// Check content type
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
const text = await response.text();
throw new Error(`Expected JSON, got ${contentType}: ${text.substring(0, 100)}`);
}
return response.json();
}
This catches the issue early and gives you a meaningful error message instead of a cryptic parse error.
Error #4: Trailing Commas -- The JavaScript Habit
The Symptom
SyntaxError: Unexpected token } in JSON at position 45
What Actually Happened
JSON does not allow trailing commas. This is valid JavaScript but invalid JSON:
{
"users": [
{ "name": "Alice" },
{ "name": "Bob" },
] // ERROR: Trailing comma after last element
}
Why This Confuses Everyone
JavaScript object literals do allow trailing commas (since ES2017):
// This is fine in JavaScript
const obj = {
name: "John",
age: 30, // Trailing comma OK here
};
We get used to this convenience in our code, then copy-paste into .json files or API payloads, and everything breaks.
My Fix
Quick fix: Remove the trailing comma.
Better approach: Use our JSON Formatter, which automatically removes trailing commas and validates the result. I keep it open in a tab whenever I am working with JSON.
Best approach: Never write JSON by hand. Generate it programmatically:
const users = [
{ name: "Alice" },
{ name: "Bob" },
];
// JSON.stringify never adds trailing commas
const json = JSON.stringify({ users }, null, 2);
console.log(json);
/*
{
"users": [
{
"name": "Alice"
},
{
"name": "Bob"
}
]
}
*/
Error #5: Invalid Escape Sequences -- The Windows Path Problem
The Symptom
SyntaxError: Unexpected token in JSON at position X
What Actually Happened
Certain characters must be escaped in JSON strings:
- Backslash:
\\ - Double quote:
\" - Newline:
\n - Tab:
\t - Unicode:
\uXXXX
The classic mistake? Windows file paths:
{
"path": "C:\new_folder\test.txt" // ERROR: \n and \t are escape sequences
}
The parser interprets \n as a newline character and \t as a tab, completely breaking the JSON structure.
How I Fix It
Option 1: Escape backslashes properly
{
"path": "C:\\new_folder\\test.txt" // Correct
}
Option 2: Use forward slashes (works on Windows too)
{
"path": "C:/new_folder/test.txt" // Also correct
}
Node.js and most Windows APIs accept forward slashes, so this is often the cleaner solution.
Option 3: Let JSON.stringify handle it
const path = "C:\\new_folder\\test.txt";
const json = JSON.stringify({ path });
// Result: {"path":"C:\\new_folder\\test.txt"}
When generating JSON programmatically, JSON.stringify() handles all escaping automatically. This is why I prefer it over manual string building.
My Actual Debugging Workflow
After years of dealing with JSON errors, I developed a systematic approach. Here is what I do now:
Step 1: Isolate the Problem
Do not try to debug in your application code. Extract the problematic JSON string:
// Log the exact string being parsed
console.log("JSON string:", jsonString);
console.log("Type:", typeof jsonString);
console.log("Length:", jsonString.length);
console.log("First 100 chars:", jsonString.substring(0, 100));
Seeing the raw string often reveals the issue immediately.
Step 2: Validate Externally
I paste the raw string into our JSON Validator. It shows:
- The exact error location (line and column number)
- Visual highlighting of syntax issues
- Plain English explanation of what went wrong
- All processing happens in your browser (no data sent anywhere)
For large payloads (larger than 10MB), I use jq in the terminal:
cat large-file.json | jq .
jq is dramatically faster than browser tools for huge files.
Step 3: Trace Back to the Source
If validation fails, I ask: where did this JSON come from?
- API response? Check the network tab, verify status codes and headers
- File read? Ensure proper encoding (UTF-8), check for BOM characters at the start
- User input? Validate and sanitize before attempting to parse
- Generated code? Review the string building logic for escaping issues
Understanding the source helps prevent the same error from happening again.
Step 4: Add Defensive Code
I now wrap all JSON parsing in try-catch blocks:
function parseJSONSafely(str, fallback = {}) {
try {
return JSON.parse(str);
} catch (err) {
console.error("JSON parse error:", err.message);
console.error("Input preview:", str.substring(0, 200));
return fallback;
}
}
// Usage
const data = parseJSONSafely(apiResponse, { default: "value" });
This prevents crashes and gives you meaningful error messages for debugging.
Quick Reference: Common JSON Mistakes
| Mistake | Example | Fix |
|---------|---------|-----|
| Trailing comma | {"a": 1,} | Remove comma: {"a": 1} |
| Single quotes | {"key": "value"} with single quotes | Use double: {"key": "value"} |
| Unquoted keys | {key: "value"} | Quote keys: {"key": "value"} |
| Comments | {"a": 1} // comment | Remove comments entirely |
| Undefined values | {"a": undefined} | Use null: {"a": null} |
| Bad escapes | "C:\test" | Escape: "C:\\test" |
| Newlines in strings | "line1<br>line2" | Use \n: "line1\nline2" |
Keep this table handy. Most JSON errors fall into one of these categories.
FAQ
Why is JSON so strict about syntax?
JSON prioritizes consistency over convenience. By being strict, it ensures that any valid JSON can be parsed identically across all programming languages and platforms. This makes it ideal for data interchange between systems, even if it is occasionally frustrating for manual editing.
Can I use comments in JSON?
No. Standard JSON does not support comments. If you need comments, consider:
- JSON5: A superset of JSON that allows comments and trailing commas
- YAML: Supports comments and is more human-readable
- Separate documentation: Keep explanatory notes in a README or schema file
Should I validate JSON on the client or server?
Both. Validate on the server for security (never trust client input). Validate on the client for better user experience (catch errors before sending to the server).
What is the difference between JSON.parse() and eval()?
Never use eval() for JSON. JSON.parse() is safer because it only parses valid JSON and does not execute arbitrary JavaScript code. Using eval() on untrusted input is a serious security vulnerability that can lead to code injection attacks.
How do I handle very large JSON files?
For files larger than 10MB:
- Use streaming parsers like stream-json
- Process in chunks rather than loading the entire file into memory
- Consider using binary formats like Protocol Buffers or MessagePack for better performance
Streaming parsers process the JSON incrementally, so you never need to hold the entire structure in memory.
Why does my API return HTML instead of JSON?
Common reasons:
- Authentication failure (redirected to login page)
- Wrong endpoint URL (returns 404 error page)
- Server error (returns 500 error page)
- Reverse proxy misconfiguration
- Rate limiting or bot protection
Always check the HTTP status code and Content-Type header before attempting to parse.
Tools That Make This Easier
JSON Validator
Our JSON Validator catches all these errors instantly:
- Pinpoints exact error location with line and column numbers
- Explains what went wrong in plain English
- Validates locally in your browser (privacy-friendly)
- Handles large files efficiently
I use this daily when debugging API responses.
JSON Formatter
Our JSON Formatter goes further:
- Beautifies minified JSON for readability
- Removes trailing commas automatically
- Syntax highlighting with real-time error detection
- Tree view for exploring complex nested structures
- Minification for production use
If you work with JSON regularly, keep this tool bookmarked.
Browser DevTools
Modern browsers have excellent built-in JSON inspection:
- Chrome/Edge: Console auto-formats JSON objects when you log them
- Firefox: Has a built-in JSON viewer addon
- Safari: Web Inspector includes JSON preview
Learn to use these. They save time.
Command Line Tools
For terminal workflows:
# Validate and format with jq
cat file.json | jq .
# Pretty-print with Python
python -m json.tool file.json
# Validate with Node.js
node -e "JSON.parse(require('fs').readFileSync('file.json'))"
I use jq constantly for quick validation in CI/CD pipelines.
Final Thoughts
JSON parse errors are inevitable. You will encounter them whether you are working with APIs, configuration files, or data imports.
The key is not avoiding them entirely -- it is developing a systematic approach to debugging them quickly.
My advice after years of dealing with these errors:
- Always check raw responses before parsing -- log the string first
- Use validators to pinpoint exact error locations -- do not scan manually
- Verify Content-Type headers -- ensure you are getting JSON, not HTML
- Add defensive code -- wrap parsing in try-catch blocks
- Stop writing JSON by hand -- use
JSON.stringify()or formatters
Most importantly: when you hit that dreaded Unexpected token error, do not panic. Print the raw response, validate it externally, and trace back to the source. The problem is usually obvious once you see the actual data.
Your future self will thank you.
Stuck on a JSON error right now? Paste your JSON into our JSON Validator -- it will show you exactly what is wrong and where. Everything runs in your browser, so your data stays private.
Or use the JSON Formatter to beautify, minify, and validate in one step. Both tools process everything locally -- no data ever leaves your machine.