Almost every JSON parse error you will ever meet is one of a dozen small mistakes, and the parser’s complaint (“unexpected token at position 4,812”) rarely names the real one. This guide lists the offenders in the order they actually occur, shows each one broken and fixed, and ends with the debugging workflow that finds them fast. To follow along, paste your payload into our JSON formatter & validator — it reports the exact line and column and shows a tree view once the document parses.
What “valid” means, briefly
JSON (RFC 8259) is a deliberately tiny grammar: objects with double-quoted string keys, arrays, strings with a fixed set of escapes, numbers, true, false, null — nothing else. No comments, no trailing commas, no single quotes, no undefined, no NaN. Most “JSON errors” are really “JavaScript-object-literal habits” leaking into a format that doesn’t allow them. A validator checks grammar only; whether the content makes sense (required fields, types) is schema validation, a separate step mentioned at the end.
The usual suspects
1. Trailing comma
{ "name": "Ada", "role": "admin", }
Legal in JavaScript, illegal in JSON. Delete the comma after the last member — of objects and arrays ([1, 2, 3,] fails the same way). This is the single most common error in hand-edited config files.
2. Single quotes
{ 'name': 'Ada' }
JSON strings use double quotes only. The fix is mechanical, but watch apostrophes inside values: 'it's' must become "it\u0027s" or, more readably, "it's" once the delimiters are double quotes — apostrophes need no escaping inside a double-quoted string.
3. Unquoted (or Python-style) keys and literals
{ name: "Ada", active: True }
Keys must be quoted strings, and the literals are true/false/null in lowercase. True, None and undefined are Python/JavaScript leaking through — a classic when a script prints a dict with str() instead of json.dumps().
4. Comments
{
// TODO: ask Ada about this
"name": "Ada"
}
JSON has no comments, full stop. If your file needs annotations, it’s probably JSONC (JSON with Comments — used by VS Code settings and tsconfig) or JSON5, both supersets that strict parsers reject. Either strip the comments or rename the file honestly and use a parser for that format.
5. Raw control characters and bad escapes in strings
{ "bio": "line one
line two" }
A literal newline inside a string is illegal; it must be \n. The same goes for tabs (\t) and any character below U+0020. The legal escapes are only \" \\ \/ \b \f \n \r \t \uXXXX. Sequences like \x41 or \' fail. This error usually comes from string concatenation in code instead of a real serializer.
6. Numbers JSON doesn’t have
NaN, Infinity, -Infinity and hex (0xFF) are JavaScript values, not JSON numbers. Serializers differ: Python’s json emits NaN by default (invalid JSON that Python itself will re-read, which hides the bug), while strict parsers elsewhere reject it. Replace with null or a string, and set allow_nan=False in Python to catch it at the source.
7. Truncated or concatenated payloads
A response cut off mid-string by a network hiccup, a file copied halfway, or two JSON documents glued together ({...}{...}) all fail near the seam. The tell-tale sign: the error position is at the very end, or at the start of the second document. Check Content-Length against what you received, and split concatenated documents before validating (log files in JSON Lines format are one document per line — validate line by line, not as a whole).
8. The invisible ones: BOM and wrong encoding
A UTF-8 byte-order mark (the three bytes EF BB BF) at the start of a file makes some parsers report an error at position 0 pointing at nothing visible. Likewise a file actually saved as UTF-16 or Latin-1 will produce garbage or “invalid token” errors. Re-save as UTF-8 without BOM; most editors show the current encoding in the status bar.
The workflow that finds them in seconds
- Validate and read the position. A good validator gives a line and column, not just “invalid”. Ours highlights the exact spot.
- Look one character before the reported position. Parsers report where they gave up, which is usually the first character that couldn’t follow the real mistake — an unclosed string three lines up surfaces at the next quote.
- Count the structure, not the text. If the position is the end of the file, suspect truncation or a missing closing bracket; collapse sections in the tree view of a formatter to check nesting pair by pair.
- Fix, then re-validate from scratch. JSON errors mask each other; the second error was invisible until the first was fixed. Expect two or three rounds on a hand-damaged file.
- Ask where it came from. Hand-edited file → the mistakes above. Machine-generated → the generator is concatenating strings or using the wrong serializer; fix the generator, not the output.
Prevention
Generate JSON with a serializer (JSON.stringify, json.dumps, Jackson, encoding/json) — never by joining strings. Keep a strict validator in CI for config files. If consumers keep sending “almost JSON”, validate against a JSON Schema after parsing: grammar says the document is well-formed, the schema says it’s the shape your code expects. And when you control the format, remember the strictness is a feature — having exactly one way to read a document is a large part of why JSON displaced looser formats in APIs, a trade-off our JSON vs YAML comparison works through in detail.