Why your JSON won't parse: a field guide.
JSON's grammar fits on an index card, which is exactly why its errors are so annoying: the mistake is always something small, human, and two characters wide. Here are the eight ways hand-written JSON actually breaks — trailing commas, wrong quotes, sneaky comments, invisible bytes — how to read the parser's unhelpful error message, and which failures aren't errors at all.
This article is about JSON that a person broke — the config file you just edited, the payload you assembled by hand, the settings blob you pasted from a wiki. (JSON that a language model broke is its own genre with its own fixes, covered in the LLM JSON problem.) Human JSON fails in remarkably predictable ways, and almost all of them come from one root cause: JSON looks like JavaScript, but it's a much stricter subset. Habits that are fine in one are syntax errors in the other.
Reading the error message first.
A typical failure looks like this:
SyntaxError: Unexpected token } in JSON at position 187
Two things to know. First, the position is where the parser gave up, not where the mistake is. A trailing comma at position 185 makes the } at 187 "unexpected" — the real problem is usually just before the reported spot. Second, "position" counts characters from the start of the whole document, newlines included, which is nearly useless for a human. Paste the input into a validator that converts position to line-and-column and points at it (the JSON Fixer does this) rather than counting characters yourself.
Trailing commas.
The single most common hand-written JSON error:
{
"name": "tooly",
"tools": 60, ← this comma has nothing after it
}
Modern JavaScript, Python, and most config formats allow a comma after the last item — it makes diffs cleaner, so editors and muscle memory insert it everywhere. JSON forbids it, full stop. Every , must be followed by another value. The mirror-image error is the missing comma between items, which usually reports as an unexpected " at the start of the next key.
Single quotes and unquoted keys.
{ 'name': 'tooly' } ✗ single quotes are not JSON
{ name: "tooly" } ✗ keys must be quoted strings
{ "name": "tooly" } ✓
JavaScript object literals accept all three; JSON accepts only the last. Strings — including every key — use double quotes, always. A related paste-borne variant: smart quotes. Copy a snippet through a word processor or a chat app and "name" can silently become “name” — typographically lovely, syntactically fatal, and nearly invisible when skimming. If a snippet that "looks right" won't parse, inspect the quote characters first.
Comments (and why JSON banned them).
{
// this comment is a syntax error in JSON
"debug": false
}
JSON has no comment syntax — deliberately. Douglas Crockford removed comments from the spec early on, explaining that people were using them to smuggle parsing directives into data, which would have wrecked interoperability. The gap was real enough that a dialect grew around it: JSONC ("JSON with comments"), which allows // and /* */ and usually trailing commas too. It's what VS Code uses for settings.json and what TypeScript accepts in tsconfig.json — which is exactly why config habits formed there break the moment you paste into something expecting strict JSON. A repair-oriented parser can strip the JSONC extras before parsing; a strict one just reports the / as an unexpected token.
Broken strings and numbers.
Literal line breaks inside strings. A JSON string must live on one line; real newlines, tabs, and other control characters must be written as escapes (\n, \t). Pasting a multi-line block of text between quotes is an instant syntax error. The same goes for a stray backslash — "C:\temp" fails because \t starts an escape sequence and \te... actually parses as a tab plus "emp", while "C:\x" fails outright. Windows paths want "C:\\temp".
Numbers that JavaScript accepts and JSON doesn't. No leading zeros (012 ✗), no bare decimal point (.5 ✗, write 0.5), no explicit plus (+1 ✗), no hex (0xFF ✗). And three JavaScript values simply don't exist in JSON at all: NaN, Infinity, and undefined. Serializers handle the gap in incompatible ways — JavaScript's JSON.stringify turns NaN into null, while Python's default json.dumps happily emits literal NaN, producing "JSON" that other parsers reject.
The invisible failures.
The byte order mark. Some Windows editors and PowerShell pipelines prepend an invisible BOM (EF BB BF) to UTF-8 files. The spec (RFC 8259) says producers must not add one, and many strict parsers — including JavaScript's JSON.parse — throw an unexpected-token error at position 0 on input that looks perfectly fine on screen. An error at the very first character of a file that displays correctly is a BOM until proven otherwise.
Other invisible characters. Non-breaking spaces where a normal space should be, zero-width spaces from web copy-paste, and curly quotes (above) all produce errors pointing at "nothing." When the visible text looks right, look at the bytes — a hex view of the first few characters (the File Inspector shows one) settles it immediately.
Two things that aren't errors.
Duplicate keys. {"a": 1, "a": 2} parses fine almost everywhere — the spec says names should be unique, not must. Most parsers silently keep the last value, so the first one vanishes without any error at all. Arguably worse than a syntax error: nothing tells you data was dropped.
Enormous numbers that parse "successfully." JSON the format places no limit on number size, but JavaScript parses numbers into 64-bit floats — so a tweet ID like 1234567890123456789 comes out of JSON.parse quietly changed to 1234567890123456800. No error, wrong data. APIs ship large IDs as strings for exactly this reason; the details are the same precision cliff covered in hex, binary, and why programmers count differently.
Takeaways.
The thing to remember: JSON is a strict subset of what JavaScript accepts — double quotes only, quoted keys, no trailing commas, no comments, escaped control characters, plain decimal numbers. The reported error position is where the parser gave up, so look just before it. And the scariest failures are the silent ones: duplicate keys and float-rounded big numbers parse without complaint.
Hand-broken JSON is a closed catalog — the eight patterns above cover essentially all of it. Once you've internalized "it looks like JavaScript but isn't," the errors stop being mysterious and start being a two-second fix.
Validate, fix, and format JSON in your browser.
The JSON Fixer validates with line-and-column error reporting, handles JSONC comments and trailing commas, pretty-prints, minifies, sorts keys, and renders a collapsible tree — entirely client-side, so your payloads never leave the machine.
Open the JSON Fixer