JSON Trailing Comma — Invisible SyntaxError at Position 284
Trailing comma after last property gives SyntaxError at pos 284.
- JSON has 6 value types: string (double quotes only), number, boolean (true/false), null, object, array. No Date, undefined, or function.
- Object: { "key": "value" } — keys MUST be double-quoted. No trailing comma after last property.
- Array: [1, 2, 3] — ordered list. Return [] for empty lists, not null (null crashes .forEach).
- Production trap: JSON.stringify() drops undefined values silently — your keys disappear with no error.
- Silent killer: price as "1999" (string) instead of 1999 (number) → "1999" + 499 = "1999499", not 2498. No error, just wrong math.
- Biggest mistake: adding comments to JSON (
// comment) — JSON has no comment syntax. Strips comments before parsing.
A single trailing comma in a JSON config file took down a fintech startup's entire deployment pipeline for four hours. Not a logic error. Not a race condition. One comma after the last property in an object — invisible to the eye, fatal to the parser. That's the world you're stepping into.
JSON — JavaScript Object Notation — is the lingua franca of the modern web. REST APIs send it. Configuration files are written in it. Databases like MongoDB store it. Mobile apps receive it. You cannot build anything networked in 2026 without touching JSON constantly. The problem isn't that it's complicated. It's that it looks deceptively simple, so people get sloppy, and sloppy JSON doesn't degrade gracefully — it throws a hard error and stops everything cold.
By the end of this, you'll be able to write valid JSON from scratch without second-guessing yourself, read a raw JSON payload and instantly spot what's wrong with it, understand exactly why each syntax rule exists, and debug the specific parser errors that make junior devs spend an hour staring at code that looks perfectly fine.
What JSON Actually Is — and Why the Rules Are So Unforgiving
Before JSON existed, developers exchanged data using XML. It looked like HTML — verbose, nested tags everywhere, an absolute nightmare to parse and read. A simple user record might take 20 lines of XML. The same data in JSON takes 5. JSON was designed by Douglas Crockford in the early 2000s as a minimal, human-readable data format that any language could parse with a trivial amount of code.
The strictness isn't arbitrary bureaucracy. JSON is designed to be parsed by machines across every programming language on the planet. If the format allowed ambiguity — JavaScript-style trailing commas, single quotes, unquoted keys — every parser would need to handle edge cases differently. Two services would argue about what a payload means. Data corruption follows. The strict rules are what make universal interoperability possible.
JSON only knows six value types: strings, numbers, booleans (true/false), null, objects, and arrays. That's it. No dates. No functions. No undefined. No comments. If you're trying to put a JavaScript Date object directly into JSON, you're going to have a bad time — and we'll cover that. First, understand the two structural building blocks everything else is built from: objects and arrays.
A key insight: JSON is a data interchange format, not a programming language. It's meant to be read by machines, not written by hand. The strictness is a feature, not a bug. Every time you manually edit a JSON file, you're at risk of introducing syntax errors. Use tools (linters, formatters, schema validators) to help you.
JSON Objects: The Key-Value Store That Powers Every API Response
A JSON object is a collection of key-value pairs wrapped in curly braces. The key is always a double-quoted string. The value is any of the six legal JSON types. Key and value are separated by a colon. Pairs are separated by commas. The last pair gets no trailing comma — this is the rule that bites people constantly.
Why no trailing comma? Because JSON was designed to be a strict subset of a specific version of JavaScript from 2001. Trailing commas weren't valid JavaScript then. The spec was frozen, and that decision became permanent. Every JSON parser in every language since then has inherited this constraint. Like it or not, that's the deal.
Objects can nest inside objects. A user object can contain an address object. That address object can contain a geo object. There's no technical depth limit — but if you're nesting more than three or four levels deep in a production API response, that's a design smell. You're probably shipping more structure than the client needs, which wastes bandwidth and makes the payload harder to consume.
null vs missing key: There's a meaningful difference between "discountCode": null and not including the discountCode key at all. null means "this field exists and has no value" — the API is explicitly telling you that no discount was applied. A missing key means "we have no information about this field" — maybe the discount system didn't respond, or maybe the field is optional. Pick one convention per field and document it. Mixing them silently breaks consumers who check if (response.discountCode) expecting a falsy value.
JSON Arrays: Ordered Lists and the Gotchas Hidden Inside Them
A JSON array is an ordered, comma-separated list of values wrapped in square brackets. The values don't have to be the same type — an array can hold strings, numbers, objects, other arrays, nulls, whatever you want. In practice, mixing types in a production array is a terrible idea because every consumer has to handle it defensively, but the spec allows it.
Arrays are zero-indexed. The first item is at index 0. This matters when you're debugging a parser error and the error message tells you the problem is 'at position 0 in the array' — it means the first element.
Where arrays get genuinely tricky in production is arrays of objects. This is the pattern behind almost every list endpoint in any REST API you'll ever call. A GET /products endpoint returns an array of product objects. A GET /orders endpoint returns an array of order objects. Each object in the array must individually follow all JSON object rules — double-quoted keys, no trailing commas, no comments. I've seen a team waste a full sprint debugging an import feature because a third-party vendor was sending an array where one object out of 500 had a trailing comma. The other 499 parsed fine. That one object silently corrupted the batch.
Empty array vs null: [] is an empty array. null is a null value. If your API returns "products": null when there are no results, every consumer has to add a null check before calling .forEach() or .map(). Someone will forget, causing TypeError: Cannot read properties of null (reading 'forEach') in production. Always return [] for empty lists. Always.
The Exact JSON Errors That Break Production Code — and How to Fix Them
JSON errors aren't subtle. The parser hits something invalid and throws immediately with a SyntaxError. The problem is the error message tells you where in the string the parser gave up — not where the actual mistake is. If your JSON is 800 lines long and the error says 'position 4721', good luck finding it without knowing what to look for.
Here are the six mistakes I've personally seen break production systems. Not hypothetical mistakes — real incidents, real error messages, real fixes. These aren't sorted by frequency. They're sorted by how long it takes a junior developer to spot them without knowing they exist.
The nastiest one isn't a syntax error at all. It's type coercion — sending a price as the string '1999' instead of the number 1999. The JSON is perfectly valid, it parses without error, and then your checkout service calculates a total of '1999' + '499' = '1999499' instead of 2498. That specific bug caused a real e-commerce platform to charge customers the wrong amount for six hours before anyone noticed. No error. No alert. Just wrong numbers.
The solution is schema validation. Tools like Zod (TypeScript), Ajv (JavaScript), or Pydantic (Python) validate the shape and types of JSON at the boundary before your business logic touches it. A simple z.object({ price: would have caught the string price immediately. Don't parse JSON and trust it. Validate it.z.number() })
| Aspect | JSON Object {} | JSON Array [] |
|---|---|---|
| Structure | Key-value pairs — each value has a name | Ordered list — values accessed by numeric index |
| Key requirement | Keys must be double-quoted strings | No keys — position is the identifier |
| Order guarantee | Order not guaranteed by spec (though most parsers preserve it) | Order is guaranteed and meaningful |
| Access pattern | parsed.customer.email | parsed.products[0].name |
| Best for | A single entity with named properties (one user, one order) | Multiple entities of the same type (list of users, list of orders) |
| Empty state | {} — empty object, zero properties | [] — empty array, zero elements |
| Nested inside each other | Objects can contain arrays as property values | Arrays can contain objects as elements |
| Type mixing allowed | Each property can have a different type | Elements can be different types — but don't do it in production |
Key Takeaways
- Trailing commas are invisible, fatal, and point to the wrong line in error messages. Lint them automatically with
jq emptyin CI. - Single quotes and unquoted keys are valid in JavaScript but invalid in JSON. Use double quotes everywhere. No exceptions.
undefinedvalues in objects are silently dropped byJSON.stringify(). Your keys vanish with no error. Replace with null if you need the field to exist.- Return
[]for empty lists, notnull.nullcrashes.forEach()and.map();[]doesn't. - A price as a string
"1999"instead of a number1999is valid JSON but corrupts arithmetic. Validate schemas at API boundaries with Zod or Ajv. - JSON has no comment syntax. Comments cause parse failures. Use a separate documentation file or JSON Schema's
$commentfield. - The safe round-trip subset is JSON's six types: string, number, boolean, null, object, array. Everything else transforms or disappears.
Common Mistakes to Avoid
- Adding a trailing comma after the last property in an object or the last element in an array
Symptom: SyntaxError: Unexpected token } in JSON at position N. The error message points to the closing brace, not the comma itself — making it hard to find.
Fix: Remove the trailing comma. Use a JSON linter (jq empty file.json) in CI to catch this automatically. Configure your editor to show trailing commas as errors. - Using single quotes for keys or string values instead of double quotes
Symptom: SyntaxError: Unexpected token ' in JSON at position 1. The parser chokes on the very first character.
Fix: Replace all single quotes with double quotes for keys and string delimiters. Single quotes inside strings (e.g., "O'Malley") are fine — they're just characters, not delimiters. Use a JSON linter or a simple regex find-replace:sed "s/'/\"/g" file.json— but be careful not to escape single quotes inside strings. - Leaving an unquoted key in an object
Symptom: SyntaxError: Unexpected token n in JSON at position 1. The error message shows the first character of the unquoted key.
Fix: Wrap all keys in double quotes. Valid:{"name": "Alice"}. Invalid:{name: "Alice"}. Many developers copy JavaScript object literals directly into JSON files — this is guaranteed to break production. - Putting comments inside JSON (// or /* */)
Symptom: SyntaxError: Unexpected token / in JSON at position N. JSON has no comment syntax — the parser sees the slash and throws.
Fix: Remove all comments. If you need to document a JSON file, use JSON Schema with the$commentkeyword (allowed by the spec specifically for this purpose), or keep documentation in a separate markdown file. Never put//or/*in production JSON — it will cause a hard parse failure. - Passing objects with undefined values through JSON.stringify() without handling them
Symptom: No error. The serialised JSON is missing keys that you expected to be present. Silent data loss.
Fix: Replace undefined with null before stringification using a replacer function:JSON.stringify(obj, (key, val) => val === undefined ? null : val). Or usenullinstead ofundefinedin the original object—nullis preserved in JSON,undefinedis not. - Storing numbers as strings in JSON (e.g., price as "1999" instead of 1999)
Symptom: No parse error. The API appears to work. But arithmetic operations produce incorrect results: `"1999" + 499 = "1999499"` instead of `2498`. This is especially dangerous in financial systems.
Fix: Enforce schema validation with Zod or Ajv at the API boundary. Define that price fields must be numbers:z.object({ price:. Never trust that a field from an external API is the right type — add explicit coercion:z.number()})const price = Number(parsed.price). - Returning null for empty arrays instead of []
Symptom: Client code that expects `.forEach()` or `.map()` on the array crashes with `TypeError: Cannot read properties of null (reading 'forEach')`. Production outage at exactly the moment the list becomes empty.
Fix: Always return[]for empty lists. If you must return null for legacy reasons, document it clearly and add client-side fallback:const items = response.items || []. Better yet, fix the API to return[].
Interview Questions on This Topic
- QWhat's the difference between
JSON.stringify()andJSON.parse()in terms of data loss? Walk me through the exact set of JavaScript values that survive a round-trip (stringify then parse) unchanged, which ones change type, and which ones disappear entirely.SeniorReveal - QYou're receiving a JSON webhook from a third-party vendor that sometimes sends price as a string (
"19.99") and sometimes as a number (19.99). You can't change the vendor. How do you defend your service against this type inconsistency without polluting your business logic with type checks everywhere?SeniorReveal - QYour team maintains a large JSON configuration file used by multiple services across the company. A junior developer adds a trailing comma after the last property, and your deployment pipeline fails with a cryptic
Unexpected token }error. How do you prevent this from happening again at the organisational level?SeniorReveal
Frequently Asked Questions
Why does JSON.parse keep throwing SyntaxError even though my JSON looks correct?
The most likely culprit is a trailing comma after the last property in an object or array — JSON.parse('{"name": "Alice",}') throws even though JavaScript itself allows trailing commas in object literals. Paste your JSON into jsonlint.com, which points to the exact line and character of the violation. The second most common cause is single quotes — JSON requires double quotes everywhere, no exceptions.
What's the difference between a JSON object and a JSON array?
An object is a named collection — you access values by their key, like parsed.username. An array is an ordered list — you access values by their position, like parsed.items[0]. Use an object when you're describing a single thing with properties. Use an array when you have multiple things of the same kind.
How do I handle a JavaScript Date object in JSON since JSON doesn't have a date type?
J automatically converts Date objects to ISO 8601 strings like SON.stringify()'2024-03-15T09:30:00.000Z'. J brings that back as a plain string, not a Date object. You have to explicitly reconstruct it: SON.parse()const createdAt = new Date(parsed.createdAt). Document in your API contract that all timestamps are ISO 8601 UTC strings and parse them at the boundary — never deeper in your business logic.
Can JSON handle circular references, and what happens in production if an object with one gets serialised?
J throws SON.stringify()TypeError: Converting circular structure to JSON the moment it encounters a circular reference. The entire serialisation fails — no partial output, no graceful fallback, just a hard error. This has burned teams who pass Express request or response objects into a logger that calls J internally. The fix is either to sanitise the object before logging using a library like SON.stringify()flatted (which serialises cycles using a path reference scheme) or safe-json-stringify, or to use structured logging libraries like Pino that handle circular references internally.
What's the difference between `null` and omitting a key entirely in a JSON API response?
"discountCode": null means 'this field exists and has no value' — the API is explicitly telling you that no discount was applied. A missing key means 'we have no information about this field' — maybe the discount system didn't respond, or maybe the field is optional. Mixing the two conventions in the same API is a design flaw. Pick one per field and document it. When consuming an API, check for both: if (response.hasOwnProperty('discountCode') && response.discountCode !== null).
That's JS Basics. Mark it forged?
6 min read · try the examples if you haven't