Why your JSON won't parse: the nine errors behind almost every failure

JSON parse errors point at a position, not a cause. Here are the nine mistakes that produce nearly all of them, why the specification rejects each one, and how to find the real culprit when the reported position is misleading.

Published · 8 min read

Unexpected token } in JSON at position 1847. The message tells you where the parser gave up, which is frequently not where the mistake is, and says nothing about what it expected instead.

The reason this is such a common frustration is that JSON looks like JavaScript object syntax but is far stricter, and almost every failure comes from that gap. Here are the nine that account for nearly all of them.

1. Trailing commas

The single most common error. JavaScript has permitted trailing commas in object and array literals since ES5, editors and formatters happily leave them, and JSON forbids them absolutely.

Invalid
{
  "name": "Ada",
  "roles": ["admin", "engineer",],
}

The parser reports its failure at the closing bracket, one character past the comma, which is why the reported position often looks like it is pointing at perfectly good syntax.

2. Single-quoted strings

JSON strings must use double quotes. Single quotes are a JavaScript convenience with no equivalent in the JSON grammar. This turns up constantly in JSON copied out of a JavaScript file, a Python dictionary, or a log line where a language printed its own repr rather than serialising properly.

Invalid → valid
{'name': 'Ada'}      ✗
{"name": "Ada"}      ✓

3. Unquoted keys

In JavaScript, an object key that is a valid identifier does not need quotes. In JSON, every key is a string and every string is double-quoted, with no exceptions — not even for keys that look like plain words.

4. Comments

JSON has no comment syntax. Douglas Crockford removed it deliberately, on the grounds that people were using comments to carry parsing directives and breaking interoperability.

This causes real confusion because several well-known files that end in .json are not JSON. tsconfig.json, .eslintrc.json, and VS Code settings are JSONC — JSON with Comments — which their own tooling parses but a standard JSON parser rejects. If a config file with comments in it works in your editor and fails in your script, this is why.

5. Numbers that are not JSON numbers

The JSON number grammar is narrow: an optional minus sign, digits, an optional fractional part, an optional exponent. Everything else is invalid, including several things that are perfectly good numbers in most languages.

  • NaN and Infinity — not representable in JSON. A serialiser that emits them produces output nothing can read back.
  • Hex, octal, and binary literals — 0xFF is not a JSON number.
  • A leading plus sign — +5 is invalid; 5 is fine.
  • A leading or trailing decimal point — .5 and 5. are both invalid; write 0.5 and 5.0.
  • Underscore separators — 1_000_000 is a JavaScript readability feature, not JSON.

6. Unescaped characters inside strings

A literal double quote, backslash, or control character inside a string must be escaped. The classic case is a Windows file path, where each backslash needs doubling.

Invalid → valid
{"path": "C:\Users\ada"}       ✗
{"path": "C:\\Users\\ada"}     ✓

Raw newlines inside a string are the other frequent offender. A multi-line value must use \n rather than an actual line break — which is exactly what happens when someone pastes a PEM certificate or a stack trace into a JSON field by hand.

7. undefined

JSON has null and no undefined. JavaScript's own JSON.stringify handles this by dropping object properties whose value is undefined and converting undefined array elements to null — which means a round trip silently changes your data's shape. Seeing a literal undefined in a JSON document means something built the string by concatenation rather than serialising it.

8. A truncated document

The parser reports an unexpected end of input. The document is not malformed so much as incomplete, and the cause is upstream: a response that hit a size limit, a write that was not flushed before the process exited, a log line clipped by a line-length cap, or a stream that was consumed twice.

If a JSON file is truncated at a suspiciously round number of bytes — exactly 65536, or exactly 1 MB — you are looking at a buffer or field limit, not a serialisation bug.

9. It was never JSON

The error is at position 0 and the token is unexpected. Nine times out of ten the response body is an HTML error page — a proxy timeout, a login redirect, a 404 from a CDN — and the client called JSON.parse on it without checking the status code or the content type.

The tell
Unexpected token < in JSON at position 0

<!doctype html>
<html><head><title>502 Bad Gateway</title>

A less obvious variant is a byte order mark. A UTF-8 BOM is three invisible bytes at the start of a file, written by some Windows editors and by Excel when it exports. The document looks perfect and fails at position 0, because the parser sees a character before the opening brace.

Finding the error when the position is misleading

The reported position is where parsing became impossible, which is often some distance after the mistake. A few things narrow it down quickly.

  1. Format the document first. A minified file has one line, so every error is on line 1 and the position number is your only clue. Formatted, the same error lands on a specific line you can look at.
  2. Check the very beginning and the very end before anything else. BOMs, HTML error pages, and truncation are all detectable in seconds and account for a large share of failures.
  3. Bisect. Delete the second half of the document, close the brackets, and reparse. Two or three rounds of this locates the problem in a large file faster than reading it.
  4. Run a repair pass. Trailing commas, single quotes, unquoted keys, and comments are all mechanically fixable, and fixing them all at once often reveals that there was only ever one real problem.

The JSON Editor does the first and last of those in one click, and reports the line and column rather than a byte offset.

The rule that prevents most of this

Almost every error above comes from JSON that was written or edited by a human, or built by string concatenation. JSON produced by a real serialiser is valid by construction.

So: do not build JSON with string templates. Build the data structure your language actually has — a dict, a map, an object — and hand it to the serialiser. Every escaping rule in this article is then someone else's problem, correctly solved.

Frequently asked questions

Why does JSON not allow comments?
They were removed from the specification deliberately. People had started using comments to carry parsing directives, which broke interoperability between implementations. Removing them made the format unambiguous. If you need commentable config, use YAML or TOML, or use JSONC and a parser that supports it.
Is a trailing comma really invalid, when my code accepts it?
Invalid per the specification. Some parsers are lenient, which is worse than either extreme — the document works in one place and fails in another. Do not rely on leniency you have not verified.
How do I represent a date in JSON?
There is no date type. The convention is an ISO 8601 string, such as 2026-08-06T12:00:00Z, parsed into a real date type on both sides. Unix timestamps as numbers are also common but ambiguous about seconds versus milliseconds — a mistake that costs a factor of a thousand.
My large ID number changed value after parsing.
JSON numbers are IEEE 754 doubles, which hold integers exactly only up to 2^53 − 1, about 9 quadrillion. Snowflake IDs and some database keys exceed that and get rounded silently. Serialise large identifiers as strings.