JSON.parse accepts one complete value under JSON grammar and throws when the text is malformed. Successful parsing does not validate application shape, trust, precision, schema version, or resource limits.
Diagnose the payload origin before editing text, validate parsed values at the boundary, handle precision-sensitive numbers deliberately, limit untrusted payloads, and monitor parser failures separately from schema failures.
The error SyntaxError: Unexpected token in JSON occurs when JSON.parse() receives invalid JSON data. JSON must follow strict formatting rules, and any deviation causes this error.
// [wrong] Problem - Invalid JSON
JSON.parse("{'name': 'John'}"); // Single quotes - Error!
// [ok] Solution - Valid JSON with double quotes
JSON.parse('{"name": "John"}'); // Works!
// [ok] Solution - Use try-catch
try {
const data = JSON.parse(jsonString);
console.log(data);
} catch (error) {
console.error('Invalid JSON:', error);
}
// Invalid JSON - single quotes
const jsonString = "{'name': 'John', 'age': 30}";
JSON.parse(jsonString); // SyntaxError!
// Also invalid - unquoted keys
const jsonString2 = "{name: 'John'}";
JSON.parse(jsonString2); // SyntaxError!
// Valid JSON - double quotes for both keys and values
const jsonString = '{"name": "John", "age": 30}';
const data = JSON.parse(jsonString);
console.log(data); // { name: 'John', age: 30 }
// Or use template literals with double quotes inside
const jsonString2 = `{"name": "John", "age": 30}`;
const data2 = JSON.parse(jsonString2);
// Already a JavaScript object
const user = { name: 'John', age: 30 };
JSON.parse(user); // SyntaxError: Unexpected token o in JSON
// Or from API that already returns object
fetch('/api/user')
.then(res => res.json()) // Already parsed here
.then(data => {
const parsed = JSON.parse(data); // Error! Already an object
});
// Don't parse if already an object
const user = { name: 'John', age: 30 };
console.log(user); // Use directly
// Check type before parsing
function safeParse(data) {
if (typeof data === 'string') {
return JSON.parse(data);
}
return data; // Already an object
}
// With fetch, don't double-parse
fetch('/api/user')
.then(res => res.json()) // Parse once
.then(data => {
console.log(data); // Use directly, don't parse again
});
// Trailing comma in object
const jsonString = '{"name": "John", "age": 30,}';
JSON.parse(jsonString); // SyntaxError!
// Trailing comma in array
const jsonString2 = '["apple", "banana", "orange",]';
JSON.parse(jsonString2); // SyntaxError!
// Remove trailing commas
const jsonString = '{"name": "John", "age": 30}'; // No trailing comma
JSON.parse(jsonString); // Works!
const jsonString2 = '["apple", "banana", "orange"]'; // No trailing comma
JSON.parse(jsonString2); // Works!
// Or clean the string before parsing
function cleanJSON(str) {
return str.replace(/,(\s*[}\]])/g, '$1'); // Remove trailing commas
}
const dirty = '{"name": "John",}';
const clean = cleanJSON(dirty);
JSON.parse(clean); // Works!
// API returns HTML error page instead of JSON
fetch('/api/user')
.then(res => res.json()) // Tries to parse HTML as JSON
.then(data => console.log(data))
.catch(err => console.error(err)); // SyntaxError!
// Or plain text response
const response = "User not found";
JSON.parse(response); // SyntaxError!
// Check content type before parsing
fetch('/api/user')
.then(res => {
const contentType = res.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return res.json();
} else {
return res.text(); // Get as text instead
}
})
.then(data => console.log(data))
.catch(err => console.error(err));
// Or check if response is OK
fetch('/api/user')
.then(res => {
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
return res.json();
})
.then(data => console.log(data))
.catch(err => console.error(err));
// Use try-catch for manual parsing
function safeParse(str) {
try {
return JSON.parse(str);
} catch (error) {
console.error('Not valid JSON:', str);
return null;
}
}
// Trying to stringify and parse undefined
const obj = { name: 'John', age: undefined };
const jsonString = JSON.stringify(obj); // '{"name":"John"}' - age is omitted
console.log(jsonString);
// Function in object
const obj2 = {
name: 'John',
greet: function() { return 'Hello'; }
};
const jsonString2 = JSON.stringify(obj2); // '{"name":"John"}' - function omitted
// Use null instead of undefined
const obj = { name: 'John', age: null };
const jsonString = JSON.stringify(obj);
const parsed = JSON.parse(jsonString); // Works!
// Remove undefined values before stringifying
function cleanObject(obj) {
return Object.fromEntries(
Object.entries(obj).filter(([_, v]) => v !== undefined)
);
}
const obj2 = { name: 'John', age: undefined, city: 'NYC' };
const clean = cleanObject(obj2);
const jsonString2 = JSON.stringify(clean); // '{"name":"John","city":"NYC"}'
// Use replacer function
const obj3 = { name: 'John', age: undefined };
const jsonString3 = JSON.stringify(obj3, (key, value) => {
return value === undefined ? null : value;
});
JSON supports objects, arrays, strings, numbers, booleans, and null under a stricter grammar than JavaScript object literals. Property names and strings use double quotes; comments, trailing commas, single-quoted strings, undefined, functions, BigInt literals, and non-finite number literals are not valid JSON.
`JSON.parse` throws SyntaxError when the complete input is not valid JSON. Messages and positions vary by engine, and a reported character can be where parsing became impossible rather than where malformed input began. Preserve a bounded, redacted sample and payload origin before transforming it.
An HTML response, proxy error page, empty body, or truncated transport is often passed to JSON.parse by mistake. Check status, content type, length, and response body origin. With fetch, `response.json()` also rejects when its body cannot be decoded as JSON; it does not make an unsuccessful HTTP status successful.
Do not repair unknown payloads by replacing quotes or removing commas. Such transformations can corrupt legitimate strings and conceal an upstream protocol defect. Fix the producer or use a parser for the actual documented format.
`JSON.parse(text)` first converts its input to a string, parses one complete JSON value, and returns JavaScript values. Leading and trailing JSON whitespace are permitted, but extra non-whitespace after the value is not. An empty string is not a JSON value.
A reviver walks parsed properties from the leaves toward the root. Returning undefined deletes a property, including possibly the root result. Use a normal function when the reviver needs its holder object as `this`; an arrow does not receive that dynamic binding.
Revivers can normalize dates, tagged values, or legacy fields, but they run after parsing and do not make malformed JSON valid. Keep transformations schema-specific and avoid converting every date-looking string or numeric-looking key. Ambiguous heuristics create silent data changes.
Parsing numbers uses JavaScript Number semantics. Large integers can lose precision before a reviver sees the numeric value. Encode precision-sensitive identifiers or integers as strings under a documented schema and convert them deliberately to BigInt or a decimal representation after validation.
Successful parsing proves syntax, not application shape. The result may be null, an array instead of an object, or an object with missing and wrongly typed fields. Validate at the boundary before accessing nested data or passing it into trusted domain code.
Define required fields, optional fields, allowed values, collection limits, string lengths, and unknown-field policy. Return diagnostics with safe field paths and categories. Avoid echoing secret values in validation errors, logs, or user messages.
Version long-lived stored or exchanged data. Migrate known older versions through explicit steps, reject unsupported future versions, and preserve rollback expectations. Clearing local storage is not a production migration strategy, and silently defaulting every missing field can mask incompatible releases.
Model success and validation failure as distinct results. A valid empty payload should remain distinguishable from malformed input, permission failure, not found, and network error. This lets callers select fallback, repair, retry, or user messaging correctly.
Parsed JSON is untrusted data. Do not merge arbitrary keys into configuration, class instances, or behavior-bearing objects. Select allowed properties into a new null-prototype or domain object and reject dangerous or unknown keys according to the schema.
Parsing a very large or deeply nested payload can consume CPU and memory before application validation runs. Enforce transport size limits, collection counts, nesting policy where possible, and timeouts at the server or gateway. Client code should also avoid retaining both huge source text and transformed copies unnecessarily.
JSON data is not safe HTML. Insert strings with textContent and apply context-specific validation to URLs and attributes. Likewise, do not construct SQL, shell commands, regular expressions, or source code from parsed fields without the correct structured API and policy.
Do not log complete malformed payloads by default. Record request ID, producer, schema version, content type, length, hash or bounded sample, and parser category. Redact credentials, personal data, session material, and nested tokens before telemetry leaves the process.
Test valid objects, arrays, scalar roots, null, empty input, truncated input, trailing commas, invalid escapes, duplicate keys according to policy, large integers, unknown fields, old versions, and maximum allowed sizes. Use fixtures produced by the real integration where possible.
Round-trip tests are useful only for values JSON can represent and for the intended normalization. Undefined object properties are omitted, unsupported array values become null in common stringification cases, dates usually serialize as strings, and object identity or prototypes are not preserved.
For network clients, test successful JSON, non-JSON error bodies, no-content responses, wrong content type, compressed or truncated transport, and cancellation. Parse according to the endpoint contract rather than calling response.json for every status.
Track parse and validation failures separately by producer and schema version. A spike in SyntaxError indicates transport or serialization trouble; a spike in schema rejection indicates a compatible-grammar contract mismatch. Alerting on those categories directs ownership to the right system.
async function readJson(response) {
const contentType = response.headers.get("content-type") || "";
const raw = await response.text();
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${raw.slice(0, 80)}`);
}
if (!contentType.includes("application/json")) {
throw new Error(`Expected JSON, received ${contentType || "unknown type"}`);
}
if (!raw.trim()) {
return null;
}
return JSON.parse(raw);
}
const validJson = '{"name":"Maya","roles":["editor"]}';
const parsed = JSON.parse(validJson);
console.log(parsed.name);
// `parsed` is already an object. Use it directly.
console.log(parsed.roles[0]);
// Invalid JSON examples:
// {'name':'Maya'} single quotes
// {"name":"Maya",} trailing comma
// {"value": undefined} unsupported value
JSON.parse() throws an error when the input string is not valid JSON. Common causes include single quotes instead of double quotes, trailing commas, parsing already-parsed objects, or trying to parse HTML/plain text.
Use double quotes for all strings, remove trailing commas, wrap in try-catch, check if data is already an object, and validate the JSON format before parsing.
JSON specification requires double quotes for strings. Single quotes are not valid JSON, even though they work in JavaScript objects. Always use double quotes in JSON.
Explore 500+ free tutorials across 20+ languages and frameworks.