Unexpected Token in JSON: Fix <, u, ], and Position 0 Errors
| Error message token | Likely cause | Quick fix |
|---|---|---|
< at position 0 | Server returned HTML (error page) instead of JSON | Check HTTP status code and endpoint URL |
u at position 0 | Parsing the word undefined (not a string) | Guard: if (str) JSON.parse(str) |
] or } | Trailing comma after last item | Remove comma before closing bracket |
o at position 1 | Passed [object Object] instead of JSON string | Use JSON.stringify(obj) first |
Single quote ' | Single-quoted strings (valid JS, invalid JSON) | Replace ' with " |
| End of JSON input | Truncated/empty response | Check response body isn't empty (204, network cut) |
You called fetch(), ran .json() on the response, and the console throws SyntaxError: Unexpected token < in JSON at position 0. The character in the message tells you what the parser found; the position tells you where it gave up — which is rarely where your mistake actually is. This guide maps every variant of this error to its real cause and the exact fix.
How to Read the Error Message
The error format is: SyntaxError: Unexpected token [CHAR] in JSON at position [N]. The character tells you what the parser found. The position is the character index from the start of the string. Some environments (Node.js 20+, modern browsers) also give a line and column number which is more useful.
| Token in error | Most likely cause |
|---|---|
< at position 0 | Server returned HTML, not JSON (see below) |
} or ] | Trailing comma before this closing bracket |
' (single quote) | Used single quotes instead of double quotes |
/ at position 0–3 | Comment in JSON (// not allowed) |
u or n | undefined or unquoted value |
| A letter at position 0 | Unquoted key or non-JSON preamble in response |
Error 1: Server Returned HTML Instead of JSON
This is the #1 cause of Unexpected token < at position 0. Your fetch or XMLHttpRequest called an endpoint, but the server returned an HTML page instead of JSON. The < is the first character of <!DOCTYPE html> or an HTML error page. The JSON parser chokes immediately.
Causes: wrong URL (hitting a 404 page), server crash (500 error page returned), auth failure (redirect to a login page), or CORS error (browser received an HTML CORS rejection).
// WRONG — assumes response is always JSON
const data = await fetch('/api/users').then(r => r.json());
// RIGHT — check status and content type first
const res = await fetch('/api/users');
if (!res.ok) {
const text = await res.text();
throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);
}
const contentType = res.headers.get('content-type') || '';
if (!contentType.includes('application/json')) {
throw new Error(`Expected JSON, got: ${contentType}`);
}
const data = await res.json();
Behaviour is identical across V8 (Chrome, Node.js) and SpiderMonkey (Firefox) — only the error wording differs, as covered below.
Always read the raw response text when debugging this error. await res.text() shows you exactly what the server sent, not what you expected it to send.
Error 2: Trailing Comma
JSON does not allow a comma after the last item in an object or array. JavaScript does — which is exactly why developers write trailing commas by habit.
// BROKEN
{"name": "Maria", "role": "admin",}
["one", "two", "three",]
// FIXED
{"name": "Maria", "role": "admin"}
["one", "two", "three"]
Tested: Node.js 22.x, Python 3.12.4, Chrome 125. Output may vary on older runtimes.
The error points to the closing } or ], not the comma. Always check the character immediately before the closing bracket when you see this error.
Error 3: Single Quotes
JSON requires double quotes for all strings — both keys and values. Single quotes are valid JavaScript but invalid JSON. This is the most common error when copying JavaScript object literals into a JSON context.
// BROKEN
{'name': 'Maria', 'role': 'admin'}
// FIXED
{"name": "Maria", "role": "admin"}
Tested: Node.js 22.x, Python 3.12.4, Chrome 125. Output may vary on older runtimes.
Error 4: Unquoted Keys
In JavaScript object literals, keys can be unquoted if they are valid identifiers. JSON requires all keys to be double-quoted strings.
// BROKEN (valid JavaScript object, invalid JSON)
{name: "Maria", role: "admin"}
// FIXED
{"name": "Maria", "role": "admin"}
Tested: Node.js 22.x, Python 3.12.4, Chrome 125. Output may vary on older runtimes.
Error 5: Comments
JSON does not support comments — not // line comments, not /* */ block comments. This error appears most often in configuration files where developers naturally want to document their settings.
// BROKEN
{
// User settings
"theme": "dark",
"language": "en" /* default */
}
// FIXED — remove comments, or use "_comment" key
{
"_comment": "User settings",
"theme": "dark",
"language": "en"
}
Tested: Node.js 22.x, Python 3.12.4, Chrome 125. Output may vary on older runtimes.
If you need comments in your config files, switch to YAML, TOML, or JSONC (JSON with Comments — supported by VS Code).
Error 6: Undefined and Non-JSON Values
JSON has six types: string, number, boolean, null, object, array. undefined, NaN, Infinity, functions, and Date objects are not valid JSON values. If you stringify them and then parse, you get this error.
// This silently produces invalid JSON
JSON.stringify({a: undefined, b: NaN, c: Infinity});
// Result: "{}" — undefined and NaN/Infinity become nothing or null
// Safe pattern
const safe = {
value: Number.isFinite(x) ? x : null,
data: item ?? null // undefined → null
};
Tested: Node.js 22.x, Python 3.12.4, Chrome 125. Output may vary on older runtimes.
Error 7: Parsing an Empty String
JSON.parse(""), JSON.parse(null), and JSON.parse(undefined) all throw this error. This happens when an API returns an empty body (like a 204 No Content) and you try to parse it.
// Guard against empty responses
function safeParse(text) {
if (!text || !text.trim()) return null;
try {
return JSON.parse(text);
} catch (err) {
console.error('JSON parse error:', err.message, 'Input:', text.slice(0, 100));
return null;
}
}
Tested: Node.js 22.x, Python 3.12.4, Chrome 125. Output may vary on older runtimes.
Error 8: BOM or Invisible Characters
A Byte Order Mark (BOM) is an invisible character () sometimes added by Windows text editors at the start of a file. It makes JSON appear to start with an invisible character before the {, causing Unexpected token at position 0.
// Strip BOM before parsing const jsonString = rawString.replace(/^/, ''); const data = JSON.parse(jsonString);
Tested: Node.js 22.x, Python 3.12.4, Chrome 125. Output may vary on older runtimes.
Error 9: Concatenated JSON Objects
Sometimes log files or streaming sources contain multiple JSON objects concatenated without any separator — this is called NDJSON (Newline Delimited JSON) or JSON Lines. JSON.parse cannot handle more than one root object.
// BROKEN — two JSON objects, not valid JSON
{"id":1,"name":"Maria"}{"id":2,"name":"Carlos"}
// FIXED — parse each line separately (NDJSON)
const objects = text
.split('
')
.filter(line => line.trim())
.map(line => JSON.parse(line));
Tested: Node.js 22.x, Python 3.12.4, Chrome 125. Output may vary on older runtimes.
Swagger UI: “is not valid JSON” Trailing Comma Error
Swagger UI throws SyntaxError: is not valid JSON when your OpenAPI spec file contains trailing commas. This is one of the most common sources of this error because developers edit swagger.json or inline API spec strings by hand, copying from JavaScript where trailing commas are legal.
// swagger.json — BROKEN (trailing comma after last path)
{
"paths": {
"/users": { "get": { "summary": "List users" } },
"/orders": { "get": { "summary": "List orders" } },
}
}
// FIXED — remove the comma after the last entry
{
"paths": {
"/users": { "get": { "summary": "List users" } },
"/orders": { "get": { "summary": "List orders" } }
}
}
Tested: Node.js 22.x, Python 3.12.4, Chrome 125. Output may vary on older runtimes.
The Swagger UI error message does not always tell you which line the trailing comma is on. Paste your spec into the ToolPry JSON Formatter — the auto-fix button strips all trailing commas in one click and shows you the clean spec you can copy back into Swagger.
If you are using Swagger UI via CDI or npm and loading the spec from a URL, the trailing comma may be in a shared config object passed to SwaggerUIBundle. Check that object definition in your JavaScript code for the same pattern.
MDN Reference: JSON.parse() and Unexpected Token Errors
The MDN documentation for JSON.parse() specifies that the method throws a SyntaxError when the string passed to it does not conform strictly to JSON syntax. The most important rule MDN highlights: JSON is not a superset of JavaScript. Specifically:
- Trailing commas are not allowed (valid in JavaScript ES5+, invalid in JSON)
- Single-quoted strings are not allowed (valid in JavaScript, invalid in JSON)
- Unquoted keys are not allowed (valid in JavaScript object literals, invalid in JSON)
- Comments are not allowed (
//and/* */are valid in JS, not in JSON) undefined,NaN, andInfinityare not valid JSON values
The error message text varies by JavaScript engine: V8 (Chrome/Node.js) says “Unexpected token , in JSON at position N”, while Firefox says “JSON.parse: unexpected character at line N column N”. The position number refers to the character offset in the original string, not the line number. The ToolPry JSON Formatter normalises this and shows you the exact line and column.
Debugging Strategy
When you hit this error: first, log the raw string before parsing — console.log(JSON.stringify(theString)) shows escape sequences and invisible characters. Second, paste the value into ToolPry's JSON Formatter, which shows the exact error location highlighted in the source. Third, check the error position and look at the character immediately before it — that is almost always where the real problem is, not the position itself.
Wrap all JSON.parse calls in try/catch in production code. A JSON parse error should never crash your application — it should be caught, logged with the raw input, and handled gracefully with a fallback.
See also: Common JSON Errors and How to Fix Them — the companion hub covering all 10 error types.