Invalid JSON vs Valid JSON — 15 Real Examples Developers Get Wrong
JSON has a deliberately strict specification — no comments, no trailing commas, no single quotes, no undefined. These constraints trip up every developer at some point. This guide covers 15 real invalid JSON examples with explanations of why they fail and the exact fix for each one.
15
common JSON mistakes covered with before/after fixes
double quotes
only valid string delimiter — single quotes always fail
null
correct replacement for undefined, NaN, Infinity
JSON.stringify()
generates valid JSON — prevents all manual mistakes
The Golden Rule: Never Build JSON Manually
Root cause of most JSON errors
Most invalid JSON comes from building it by hand — concatenating strings, copying JavaScript object literals, or manually editing JSON files. The fix: always use JSON.stringify() to generate JSON and JSON.parse() to consume it. The library handles quoting, escaping, and formatting correctly every time.
Mistakes #1–5: Syntax Violations
Single quotes — SyntaxError
// ❌ Single quotes — JSON requires double quotes everywhere
{'name': 'John', 'age': 30}
// The JSON spec (RFC 8259) explicitly requires double-quoted strings.
// JavaScript allows single quotes in object literals, but JSON does not.Double quotes required for all strings
// ✅ Double quotes for all strings and keys
{"name": "John", "age": 30}
// Note: numbers, booleans (true/false), and null are NOT quoted:
{"name": "Alice", "active": true, "score": 95.5, "missing": null}Trailing comma — not allowed in JSON
// ❌ Trailing comma before closing brace or bracket
{
"name": "John",
"age": 30,
}
// Also invalid in arrays:
[1, 2, 3,]
// JSON.parse throws: "Unexpected token }" or "Unexpected token ]"Remove trailing comma + use JSON.stringify()
// ✅ No trailing comma — last item has no comma
{
"name": "John",
"age": 30
}
// Arrays too:
[1, 2, 3]
// Prevention: JSON.stringify() never produces trailing commas.
const json = JSON.stringify({ name: "John", items: [1, 2, 3] }); // always validComments — JSON.parse throws on // or /* */
// ❌ Comments — JSON has no comment syntax
{
// This is the user object
"name": "John", /* display name */
"age": 30
}No comments in JSON — use JSONC or JSON5 if needed
// ✅ Remove all comments from JSON
{
"name": "John",
"age": 30
}
// If you need comments in config files, use JSONC (VS Code settings.json)
// or JSON5 — parsed with the json5 npm package: JSON5.parse(text)Unquoted keys — JavaScript syntax, not JSON
// ❌ Unquoted keys
{
name: "John", // missing quotes around key
age: 30
}
// JavaScript object literals allow unquoted keys. JSON does not.
// JSON requires ALL keys to be double-quoted strings.All JSON keys must be double-quoted strings
// ✅ All keys must be double-quoted strings
{
"name": "John",
"age": 30
}
// Even numeric-looking keys must be quoted in JSON:
// {"42": "the answer"} ← valid JSON
// {42: "the answer"} ← invalid JSONMissing closing brace — truncated JSON
// ❌ Missing closing brace or bracket
{
"users": [
{"name": "John"}
]
// Missing } at the end
// JSON.parse throws: "Unexpected end of JSON input"Balanced braces and brackets
// ✅ Every opening delimiter needs a matching close
{
"users": [
{"name": "John"}
]
}
// Tip: Use a JSON validator to find the exact line of the missing brace.
// Most parsers tell you the position: "at position 47" — count from there.Mistakes #6–10: Invalid Values
| Item | Invalid JSON Value | Valid Replacement + Reason |
|---|---|---|
| NaN | {"price": NaN} | {"price": null} — NaN is a JavaScript value, not valid JSON. Replace with null or -1 as sentinel. |
| Infinity | {"count": Infinity} | {"count": null} — JSON has no Infinity. JSON.stringify() converts Infinity to null automatically. |
| undefined | {"middle": undefined} | Omit the key, or use null. JSON.stringify() drops undefined keys entirely from objects. |
| Function | {"fn": function() {}} | JSON cannot store functions. JSON.stringify() drops function-valued keys. Use a string descriptor instead. |
| Date object | {"date": new Date()} | {"date": "2025-01-15T00:00:00.000Z"} — Serialize dates to ISO 8601 strings before stringifying. |
// JSON.stringify's behavior with invalid values:
const obj = {
name: "Alice",
score: NaN, // → null in JSON
count: Infinity, // → null in JSON
middle: undefined, // → key omitted entirely
handler: function() {} // → key omitted entirely
};
JSON.stringify(obj);
// Result: {"name":"Alice","score":null,"count":null}
// undefined and function keys are silently dropped!
// ✅ Fix: use a replacer to control serialization explicitly
JSON.stringify(obj, (key, value) => {
if (value === undefined) return null; // convert undefined → null
if (typeof value === 'number' && !isFinite(value)) return null; // NaN/Infinity → null
if (typeof value === 'function') return undefined; // omit functions
return value;
});
// ✅ Fix: convert Date objects before stringifying
const event = {
name: "Launch",
date: new Date('2025-06-01').toISOString() // "2025-06-01T00:00:00.000Z"
};
JSON.stringify(event); // {"name":"Launch","date":"2025-06-01T00:00:00.000Z"}Mistakes #11–15: Structure Errors
| Item | Mistake | Fix and Explanation |
|---|---|---|
| Unescaped quotes | {"msg": "He said "Hello""} | {"msg": "He said \"Hello\""} — Backslash-escape quotes inside strings. JSON.stringify handles this automatically. |
| Missing comma | {"name": "John" "age": 30} | {"name": "John", "age": 30} — Properties must be comma-separated. Easy to miss when adding new fields. |
| Trailing comma in array | [1, 2, 3,] | [1, 2, 3] — Same rule as objects: no trailing comma before the closing ]. |
| Multiple root objects | {"a":1}{"b":2} | [{"a":1},{"b":2}] — JSON must have exactly one root element. Wrap multiple objects in an array. |
| Octal notation | {"code": 0123} | {"code": 83} — Octal integer literals are not valid in JSON. Use decimal or hex string representation. |
Complete Reference: JSON Allowed vs Disallowed
| Item | ✅ Valid in JSON | ❌ NOT Valid in JSON |
|---|---|---|
| Strings | "double-quoted string" | 'single-quoted', `template literal`, unquoted |
| Numbers | 42, -7, 3.14, 1e5, -2.5e-3 | NaN, Infinity, -Infinity, 0123 (octal), 0xFF (hex) |
| Booleans | true, false (lowercase) | True, False, TRUE, FALSE, 1, 0 |
| Null | null (lowercase) | undefined, NULL, Null, nil, None |
| Keys | "quoted-key", "123", "key with spaces" | unquoted, 'single-quoted', 123 (number key) |
| Comments | (none allowed) | // line comment, /* block comment */, # hash |
| Trailing commas | (none allowed) | {"a": 1,}, [1, 2,] |
| Special values | null for missing/unknown | undefined, NaN, Infinity, functions, symbols |
Fixing Invalid JSON Programmatically
/**
* Attempt to repair common JSON mistakes before parsing.
* Handles: single quotes, trailing commas, unquoted keys,
* undefined/NaN/Infinity values, JavaScript-style comments.
* Note: this is a best-effort heuristic — use JSON5 library for full support.
*/
function repairJson(input) {
let s = input.trim();
// 1. Strip // and /* */ comments
s = s.replace(///[^
]*/g, '');
s = s.replace(//*[sS]*?*//g, '');
// 2. Replace undefined/NaN/Infinity with null
s = s.replace(/:s*undefined/g, ': null');
s = s.replace(/:s*NaN/g, ': null');
s = s.replace(/:s*-?Infinity/g, ': null');
// 3. Remove trailing commas before } or ]
s = s.replace(/,s*([}]])/g, '$1');
// 4. Quote unquoted keys (simple heuristic — may fail on complex cases)
s = s.replace(/([{,]s*)([a-zA-Z_$][a-zA-Z0-9_$]*)s*:/g, '$1"$2":');
// 5. Try to parse the repaired string
try {
return JSON.parse(s);
} catch (e) {
// If still failing, use json5 library for more robust parsing
throw new Error(`Could not repair JSON: ${e.message}. Try the JSON5 library.`);
}
}
// Example:
const broken = `{
// user profile
name: 'Alice',
score: NaN,
active: true,
}`;
const repaired = repairJson(broken);
// { name: "Alice", score: null, active: true }
// For production use, prefer the json5 or jsonrepair npm packages:
// import { jsonrepair } from 'jsonrepair';
// const valid = jsonrepair(broken);Read the error message — it tells you exactly where
JSON.parse() errors include the position: "Unexpected token ',' at position 47". Count characters from the start of your string to find the exact problem. In browsers, the DevTools console shows the error with a preview of the surrounding content.
Print the raw string before parsing
Add console.log(JSON.stringify(rawString)) before the JSON.parse() call. JSON.stringify wraps the string in quotes and escapes special characters, making invisible characters (BOM, zero-width spaces, non-UTF-8 bytes) visible.
Paste into a JSON validator
Online validators (jsonlint.com, our JSON Validator) highlight the exact line and character of the first error. Much faster than counting positions manually in a large JSON blob.
Check the source — where is the JSON coming from?
API response? Check the raw HTTP body with curl -v or Chrome DevTools Network tab → Response. File? Check encoding (must be UTF-8, no BOM). User input? Always validate before parsing and wrap in try/catch.
Use JSON.stringify() going forward
After fixing the immediate problem, eliminate the root cause: switch from manual JSON string building to JSON.stringify(). It handles all escaping, quoting, and formatting automatically, making it impossible to produce most of these errors.
Use JSON.stringify() to generate, JSON.parse() to consume