You edit a config file and the app won't start. You paste an API response and get "invalid JSON." It looks fine to your eyes.
JSON has a very narrow grammar. Most things that work in JavaScript do not work in JSON. This post runs 12 cases through an actual parser and sorts out what gets caught and what quietly passes.
1. JSON is not JavaScript
Start with the three you'll hit most often. All three are perfectly legal JavaScript.
| What you wrote | JS | JSON |
|---|---|---|
{"a": 1,} (trailing comma) |
✅ | ❌ |
{'a': 1} (single quotes) |
✅ | ❌ |
{"a": 1} // note (comment) |
✅ | ❌ |
Trailing commas are the most common — you delete the last item in a list and the comma stays behind. Single quotes ride along when you copy from code. Comments happen when you try to document a config file.
Keys must be wrapped in double quotes too. {a: 1} is a JavaScript object literal, not JSON.
If you really need comments, JSON5 and JSONC exist. But those are supported by specific tools (like
tsconfig.json), not by standard JSON. Feed one to a strict parser and it fails.
2. When the error tells you where — and when it doesn't
Parser messages split into two kinds. These are actual results.
Gives you a position
| Input | Message |
|---|---|
{"a":1,} |
Expected double-quoted property name ... (line 1 column 8) |
{'a':1} |
Expected property name or '}' ... (line 1 column 2) |
{"a":01} |
Unexpected number ... (line 1 column 7) |
Doesn't
| Input | Message |
|---|---|
{"a":NaN} |
Unexpected token 'N' ... is not valid JSON |
{"a":.5} |
Unexpected token '.' ... is not valid JSON |
The difference is how far the parser got before giving up. If it was following the structure and hit a wall, it can count the line and column. If it ran into an unexpected token and stopped immediately, you get the character but no position.
So when you see line 1 column 8, that's almost certainly the spot. When you only get Unexpected token 'N', search the document for values starting with N — nine times out of ten it's NaN.
3. 🔴 Only one thing passes silently: duplicate keys
This is the important part. Of the 12 cases, 11 raised errors. Exactly one went through without a word.
{"a": 1, "a": 2} → {"a": 2}
No error, no warning. The later value simply wins and the 1 is gone.
The JSON spec says duplicate names are discouraged but does not forbid them, so most parsers take the last value and move on.
This matters because it's the shape most likely to bite you in a config file.
- You edited a setting by adding a new line below without deleting the old one → the lower value applies
- You added it above instead → your change is ignored and the old value applies
- You merged two config files and a key collided → one side is buried entirely
Since nothing errors, you end up asking "the syntax is valid, so why isn't my change taking effect?" If you edited a file and behavior didn't change, check for a repeated key first.
4. Numbers that trip you up
Numbers are where JS and JSON diverge most.
| Input | Result | Why |
|---|---|---|
NaN |
❌ | Not a JSON value |
Infinity |
❌ | Same |
01 |
❌ | Leading zeros not allowed |
.5 |
❌ | Needs the leading zero (0.5) |
NaN and Infinity come up a lot — they appear when you serialize computed results, and standard JSON has no way to represent them at all. The usual fix is emitting null instead.
One more while we're on numbers. Very large integers pass the syntax check but can change value. JavaScript safely handles integers up to about 9 quadrillion, so a larger ID parsed as a number comes back with different trailing digits. That's why many APIs send long IDs as strings.
5. The things you can't see
Some causes are invisible no matter how hard you stare.
BOM (Byte Order Mark) — an invisible marker at the start of a file. Windows Notepad and Excel exports add it readily. Nothing shows on screen, but the parser throws Unexpected token. If the error points at the very beginning, suspect this.
Backslashes — Windows paths break things. "C:\Users" tries to read \U as an escape and fails; you need "C:\\Users". JSON only knows \" \\ \/ \b \f \n \r \t \uXXXX, so something like \x41 gives you Bad escaped character.
Line breaks — you can't press enter inside a string. Use \n.
6. How to actually check
- Read the line and column first. When a position is given, it's usually right there or just before.
- No position? Suspect a value.
NaN,Infinity, or an unquoted value are the candidates. - Valid syntax but wrong behavior? Look for duplicate keys. The parser will not help you here.
- Format it. Nobody can match brackets in a single compressed line. Indentation alone surfaces the missing one.
- Validate config files before deploying. A broken
package.jsonortsconfig.jsonstops the whole build.
To check something right now, try the JSON Formatter. It switches between 2- and 4-space indentation and one-line minification, and points to the line and column of any syntax error. There's a collapsible tree view for deeply nested structures too. Everything runs in your browser, so what you paste never leaves your machine — which matters when you're inspecting production data.
If you're dealing with mangled encodings, the Encoder/Decoder is the neighboring tool; to spot what changed between two responses, try the Diff Checker.
Summary
- JSON is not JavaScript — trailing commas, single quotes, and comments are the three most common mistakes
- Error messages split into positioned and unpositioned. No position usually means a value like
NaN - 🔴 Duplicate keys are not an error — the later value wins and the earlier one vanishes. Check here first when a config change doesn't take
NaN,Infinity,01, and.5all violate the grammar, and very large integers pass but may change value- BOM and backslashes are the causes you cannot see