The 20 ways a pasted JSON breaks
A document that was JSON when it left a process rarely arrives as JSON. The ways it breaks are few, and they are the same ones every time.
Where broken JSON comes from
Almost never from a JSON serialiser. It comes from a language’s own object syntax — a Python repr, a JavaScript object literal pasted out of a source file, a Ruby hash — from a config dialect that allows comments and trailing commas, from a hand edit that dropped a comma, or from a log line cut at a buffer boundary. Each origin breaks the text in a characteristic way, and each way is one mechanical fix.
The 20 steps, from the engine
These are the repair steps the JSON adapter registers, listed from the engine when this page was built, in the order they run. A step marked as a judgment call can say something your document did not, so it is reported and never applied unless you ask.
- Strip leading byte-order mark (BOM)
- Normalise common encoding glitches (mojibake)
- Escape string values whose content is raw JSON
- Escape raw newlines and embedded `"` inside string values
- Split `"key: "value"` into `"key": "value"`
- Strip `//` and `/* */` comments (not legal JSON)
- Replace 'single' with "double" quotes
- Insert missing commas between sibling values
- Insert missing `:` between property name and value
- Quote bare identifier keys
- Remove trailing commas before } or ]
- Insert `null` for empty value slots in objects/arrays — a judgment call
- Replace JS/Python literals (True, False, None, NaN, Infinity, undefined) with JSON equivalents — a judgment call
- Collapse one level of double-escaping — a judgment call
- Convert literal newlines/tabs in strings into proper JSON escapes
- Repair broken `\uXXXX` escapes inside string literals
- Canonicalise non-JSON number literals (leading dot, octal, etc) — a judgment call
- Quote bare identifier values (`production` → `"production"`)
- Insert missing close brackets when an array/object closes with the wrong glyph — a judgment call
- Drop content after the document root — reported, applied only when asked
What that looks like on one file
The webhook sample above — a Stripe event printed from Python and edited by hand — needs 46 fixes in 7 kinds: 31 × quote unquoted key, 7 × remove trailing comma, 3 × single → double quotes, 2 × python false → json false, 1 × insert missing comma after value, 1 × python true → json true, 1 × python none → json null. Fix all applies them, greens the lines it touched, and leaves every other byte as pasted; Undo puts the original back exactly.
What no step does: guess at content. A value that is missing stays missing and is listed as a problem with its line. Fixing syntax is mechanical; inventing data is not, and the list is where the two are kept apart.