Why is my JSON invalid? Why is your JSON invalid? Because JSON (RFC 8259) is stricter than JavaScript — one trailing comma, single quote, comment, or unquoted key breaks the parser and throws “Unexpected token” at the exact column. Unlike JS objects, JSON allows only double-quoted strings and keys, no comments, no trailing commas, no functions, and no undefined — that strictness is the whole point. This guide explains what valid JSON actually requires, the 7 syntax errors that fail every validator, how to read “Unexpected token at position 42,” and the exact fix for each without guessing — with live validators from our JSON tools.
- Valid JSON per RFC 8259 + json.org: Root is object
{}or array[], keys double-quoted, strings double-quoted, numbers without leading zeros,true/false/nulllowercase, no trailing commas, no comments, no single quotes, no functions. - Top 7 fails: 1) Trailing comma
{"a":1,}, 2) Single quotes{'a':1}, 3) Comments// or /* */, 4) Unquoted keys{a:1}, 5) Single-quoted string or unescaped newline, 6) Leading zero /NaN/Infinity, 7) Missing comma or extra comma, duplicate keys. See MDN JSON.parse + JSON bad parse. - Read the error:
Unexpected token } at position 42means the parser expected a string key or value but found}— usually a trailing comma before it. Count from 0, or paste into a validator to highlight column. - Fix loop (30 sec): Paste into validator (Toolwasp JSON tools or JSONLint), fix first error only (later errors are cascade), re-validate → 10/10, then run formatter to canonical spacing. See MDN JSON + ECMA-404.
- Rule of thumb: If it’s valid JS object literal but invalid JSON — it’s probably trailing comma, single quote, or comment. JSON is a subset of JS, not the same (MDN).
Why Is My JSON Invalid? What Valid JSON Actually Requires (RFC 8259)
Valid JSON is a single value — usually an object or array — written exactly as RFC 8259 defines, with no allowances for JavaScript habits. Douglas Crockford’s json.org diagram and RFC 8259 are the law: one JSON text, one value.
I see developers treat JSON like “JS without the code” and then wonder why JSON.parse('{"a":1,}') throws. It’s because JSON is deliberately strict so every language can parse it identically — no comments to ignore, no single quotes to normalize, no trailing commas to forgive. The ECMA-404 JSON Data Interchange Syntax (ECMA-404) says the same: strict, double-quoted, no extensions.
| Rule (RFC 8259) | Valid | Invalid (Throws) | Why |
|---|---|---|---|
| Root | {"a":1} or [1,2] | {a:1} (no root quotes?) / multiple roots | One value only |
| Keys | {"key": 1} | {key: 1} {'key':1} | Must be double-quoted string |
| Strings | "hi\n" | 'hi' "hi "line\nbreak" (literal newline) | Double quotes + escape |
| Numbers | 0 42 -3.14 1e10 | 007 NaN Infinity | No leading zeros, no non-finite |
| Booleans/null | true false null | True FALSE NULL undefined | Lowercase only |
| Punctuation | {"a":1, "b":2} | {"a":1,} [1,,2] | No trailing commas |
| Comments | (none) | // hi /* hi */ | Not allowed in JSON |
Bottom line: if you can paste it into MDN JSON.parse without throwing (JSON.parse(str)), it’s valid. If it throws SyntaxError: Unexpected token (MDN JSON bad parse), the position points to the first violation — fix that one first, because everything after is cascade.
JSON vs JavaScript Object Literal — Why Valid JS Still Fails JSON
JS allows {a:1, b:2,} (unquoted keys, trailing comma), {'a':1} (single quotes), {a:1 // comment}, {x: undefined, y: NaN} — JSON allows none. That’s why copying a JS object from console and pasting into a .json file often fails. JSON is data, not code — no functions, no undefined, no comments.
The 7 Common Syntax Errors That Invalidate JSON (And the Exact Fix)
Fix these seven and 95% of “why is my JSON invalid” goes away — count from 1 to 7 in any validator and you’ll hit them.
1. Trailing Comma — The #1 Reason
JSON forbids a comma before } or ] — JS forgives it, JSON does not.
// Invalid — trailing comma
{
"name": " Ada ",
"age": 30,
}
// Valid — no trailing comma
{
"name": " Ada ",
"age": 30
}
Error: Unexpected token } at position 42 — the } is unexpected because parser expected a string key after the comma. Fix: delete the comma. Tip: enable “trailing comma” lint in your editor for .json only — it’s valid in JS/TS but not JSON. Our JSON tools highlight the trailing comma in red.
2. Single Quotes Instead of Double
JSON strings and keys must be double-quoted — single quotes are a JS habit.
// Invalid
{'name': 'Ada', 'active': true}
// Valid
{"name": "Ada", "active": true}
Error: Unexpected token ' at position 1 — the ' is not a valid JSON delimiter. Fix: replace all ' with " — but be careful: if string contains " inside, escape it as \". Validator’s replace is safer than regex find/replace.
3. Comments — // and /* */ Are Not Allowed
JSON has no comments — not even “just one” for notes. Many copy-paste configs include // TODO and then fail.
// Invalid
{
// user
"name": "Ada" // name
}
// Valid — remove comments
{
"name": "Ada"
}
If you need comments, use JSON5 or JSONC, or keep a "_comment": "user — remove before parse" field — but the file sent to JSON.parse must be comment-free per RFC 8259.
4. Unquoted Keys or Bare Words
Every key must be a double-quoted string — {age: 30} is JS, not JSON.
// Invalid
{ name: "Ada", active: true, null: "oops" }
// Valid
{ "name": "Ada", "active": true, "null": "oops" }
Even null as a key must be quoted — otherwise parser reads it as the literal null value where a string was expected. Error: Unexpected token n at position 2.
5. Unescaped Newlines, Tabs, or Control Characters Inside Strings
Strings cannot contain literal newline or unescaped control chars — must be \n, \t, \", \\.
// Invalid — literal newline in string
{
"bio": "line one
line two"
}
// Valid — escaped
{
"bio": "line one\nline two"
}
// Valid — actual multi-line via array
{
"bio": ["line one", "line two"]
}
Copying a multi-line Excel cell directly into a JSON string often injects a literal \r — escape it or use a formatter that auto-escapes.
6. Bad Numbers — Leading Zeros, NaN, Infinity, Hex
Numbers must be decimal without leading zeros, and NaN/Infinity are not JSON.
// Invalid
{"zip": 007, "score": NaN, "big": Infinity, "hex": 0xFF}
// Valid
{"zip": 7, "zip_str": "007", "score": null, "hex": 255}
If you need leading zeros (ZIP, phone), store as string "007". For impossible numbers, use null per your schema.
7. Missing Comma, Extra Comma, or Duplicate Keys
Every member needs exactly one comma between, none at end, and duplicate keys are legal but the last wins — validators warn.
// Invalid — missing comma
{"a": 1 "b": 2}
// Invalid — extra comma in array
[1, 2,, 3]
// Valid
{"a": 1, "b": 2}
[1, 2, 3]
// Warning — duplicate key (last wins, but confusing)
{"a": 1, "a": 2} // → {"a":2} — don’t do it
How to Read “Unexpected token at position X” and Fix in 10 Seconds
The position counts characters from 0, including whitespace — paste into a validator that highlights column X and you see the exact spot.
JSON.parse('{"a":1,}')
// SyntaxError: Unexpected token } in JSON at position 7
// 01234567
// ^ position 7 is } — expected " after the comma
JSON.parse("{'a':1}")
// SyntaxError: Unexpected token ' in JSON at position 1
// ^ single quote where " expected
JSON.parse('{"a": 1 // comment}')
// SyntaxError: Unexpected token / in JSON at position 8
Use MDN JSON bad parse as the error catalog: each token tells the expectation. Count with a monospaced view or let our JSON tools jump to column — the red underline is position X.
Quick CLI Check (Node/Python)
// Node
node -e "JSON.parse(require('fs').readFileSync('data.json','utf8'))" && echo "valid"
# Python
python -m json.tool data.json > /dev/null && echo "valid" || python -m json.tool data.json
# or
python -c "import json,sys; json.load(open('data.json')); print('valid')"
Tools That Fix It — Validator vs Formatter vs Linter
| Tool | What It Does | When to Use | Example |
|---|---|---|---|
| Validator | Checks strict RFC 8259 — highlights first fail | “Why invalid?” | Toolwasp JSON Validator, JSONLint |
| Formatter / Beautifier | Pretty-prints with 2-space indent, escapes newlines | After valid — canonical spacing | Toolwasp Format, python -m json.tool |
| Linter / IDE | Live red underline as you type | While authoring | VS Code JSON language mode |
| Converter | Fixes common JS→JSON drift (quotes, commas) | Bulk fixing JS object dumps | Toolwasp JSON Tools → Fix |
Workflow I use: paste → validator highlights trailing comma at line 12 col 18 → fix → re-validate → formatter → copy. For bulk files, jq is king: jq . data.json pretty-prints or errors with line/col; jq -s . for streaming. For CI, add python -m json.tool data.json > /dev/null as a pre-commit hook — fails the commit if invalid before it ships.
VS Code — Turn On Strict JSON for This File
Set language mode to JSON (not JSONC) for .json files — that enables trailing-comma and comment errors. For configs that allow comments, rename to .jsonc (JSON with Comments) and keep strict for data files. See MDN JSON for stringify/parse nuance.
JSON vs JSONC vs JSON5 vs YAML — When Strict Is Overkill
| Format | Allows | Parser | Use For |
|---|---|---|---|
| JSON | Strict only — RFC 8259 | JSON.parse | APIs, data interchange — must be strict |
| JSONC | Comments + trailing commas (VS Code) | VS Code, not JSON.parse | tsconfig.json, configs |
| JSON5 | Single quotes, unquoted keys, trailing commas, comments | json5 npm | Human-written configs |
| YAML | Comments, unquoted, multiline | yaml parser | K8s/Docker configs |
If your file needs comments, don’t force JSON — use JSONC/YAML. But the wire format you send to fetch must be strict JSON — convert before sending. Our JSON tools include a JSON→JSON5 stripper and a YAML→JSON converter for this reason.
Real-World Broken JSON — Copy-Paste From Console, APIs, and Spreadsheets
Most invalid JSON in the wild isn’t typed — it’s pasted from somewhere that wasn’t JSON to begin with.
From console.log: You console.log({a:1, b: undefined}) and copy the output { a: 1, b: undefined } — that’s a JS object preview, not JSON. It has unquoted keys and undefined. Fix: in the console run copy(JSON.stringify(obj, null, 2)) — that copies strict JSON to clipboard.
From an API that returns JSON5-like text: Some backends (especially Node) log with single quotes or trailing commas for readability. If you save that log to .json and later JSON.parse it, it fails. Always capture raw response via Network tab → Response → Copy, not the console preview. In Node, ensure server does res.json(obj) (which calls JSON.stringify) not res.send("'" + obj + "'") .
From Excel / Google Sheets: Export CSV → convert to JSON via a script that forgets to quote keys or leaves a trailing comma on the last row. The last-row comma is invisible until validator highlights it. Use a converter that guarantees RFC 8259 — our JSON tools include CSV→JSON with correct quoting and no trailing comma.
From Python: Python’s str(dict) looks like JSON but isn’t: {'a': True, 'b': None} uses single quotes, capital True/None. Fix: use json.dumps(obj) in Python — it emits double quotes and lowercase true/null. Similarly, Ruby’s puts hash is not JSON.
From LLMs / AI outputs: ChatGPT often wraps JSON in markdown fences ```json ... ``` and adds comments like “// explanation”. Strip fences and comments before parsing. A validator that shows “Unexpected token ` at position 0” usually means you pasted the fences too.
How to Bulletproof Your Pipeline — Validate Early, Format Late
Add a pre-commit hook that fails if any .json is invalid. Minimal .pre-commit-config.yaml:
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: json-validate
name: Validate JSON (RFC 8259)
entry: python -m json.tool
language: system
files: \.json$
args: [--check]
Or with jq: jq empty data.json exits 4 on invalid and prints line/col. In JavaScript CI: node -e "require('fs').readdirSync('.').filter(f=>f.endsWith('.json')).forEach(f=>JSON.parse(require('fs').readFileSync(f,'utf8')))" && echo "all valid" — if any file throws, CI fails before deploy. Formatting on save: VS Code → Settings → Editor: Format on Save → enable, and set Default Formatter to built-in JSON for .json. That auto-removes trailing commas on save.
Why JSON Is Strict — The Design Choice That Saves You
Strictness is a feature, not a bug — it makes JSON predictable across 20 languages. If JSON allowed single quotes, trailing commas, and comments like JS does, every parser would need to guess which extensions are enabled — Python’s json would disagree with Go’s encoding/json on the same file. By forbidding extensions, RFC 8259 guarantees {"a":1} means the same in Python, Go, Rust, Java, and JS. That’s why linters exist for JS (ESLint can allow trailing commas) but validators exist for JSON (must be strict). When you want human convenience, use JSONC/JSON5/YAML at author time, then compile to strict JSON for the wire — that separation keeps both ergonomics and interoperability.
History: JSON was derived from JS literal syntax in 2001, but Crockford removed the permissive parts to make it a data interchange format, not a programming language. ECMA-404 formalized that strict subset in 2013; RFC 8259 updated it in 2017 to clarify UTF-8 and duplicate-key handling. Knowing that history helps you remember: JSON is not “JS without functions” — it’s a separate spec that happens to look like JS. That’s why copying a console.log preview that shows {a: 1} and pasting it into a validator fails — the preview is JavaScript’s rendering, not the JSON string that JSON.stringify would emit. Always copy via the Network response body or copy(JSON.stringify(obj)) to get strict text.
Best Practices Checklist — Keep JSON Valid From the Start
| Check | Do | Don’t |
|---|---|---|
| Quotes | Double-quote keys + strings, escape \" inside | Single quotes, unquoted keys |
| Commas | Exactly one between members, none at end | Trailing , before }/] |
| Comments | Remove before parse; use _comment field if needed | // /* */ in .json |
| Numbers | 42 0 -3.14 — stringify ZIP as "007" | 007 NaN Infinity |
| Encoding | UTF-8, escape control chars (\n \t) | Literal newline in string |
| Tooling | Validate in CI (python -m json.tool), format on save | Hand-edit 10KB JSON without linter |
Automate: add jq . data.json to pre-commit; in JS, always create JSON via JSON.stringify(obj) instead of hand-typing — it never emits trailing commas or single quotes. For hand-authored configs, start from a validator template in our JSON tools category — paste, fix red, format, copy.
Practice Lab — Fix 3 Invalid JSONs in 2 Minutes
# Lab — copy each block into https://toolwasp.com/category/json-tools validator
1) Trailing comma:
{"name": "Ada", "age": 30,}
→ Delete comma before } → {"name":"Ada","age":30}
2) Single quotes + comment:
{
// user
'name': 'Ada'
}
→ {"name":"Ada"} (remove //, replace ' with ")
3) Bad number + missing comma:
{"zip": 007, "city": "Paris" "country": "FR"}
→ {"zip": "007", "city": "Paris", "country": "FR"}
For each: validator highlights first error → fix → re-validate → format → done.
You just fixed the exact 7 errors that cause 95% of “why is my JSON invalid” — trailing comma, single quotes, comments, unquoted keys, bad numbers, missing commas, and unescaped newlines. Repeat once and the pattern sticks.
Frequently Asked Questions
Why is my JSON invalid when it looks fine?
Because it’s valid JavaScript but not valid JSON — the most common culprit is a trailing comma before } or ], which JS allows but RFC 8259 forbids. Next are single quotes, comments, or unquoted keys. Paste into a validator (Toolwasp JSON tools) — it highlights the exact column (e.g., Unexpected token } at position 42 is the trailing comma).
What does “Unexpected token } at position X” mean?
The parser expected a string key or value but found } — usually because a trailing comma told it “another member is coming” then the object ended. Position counts from 0 including whitespace. For {"a":1,}, position 7 points at } after the comma. See MDN JSON bad parse.
Can JSON have comments or trailing commas?
No — strictly per json.org and RFC 8259, JSON has no comments and no trailing commas. For configs that need comments, use JSONC (VS Code) or JSON5/YAML and convert to strict JSON before calling JSON.parse. See MDN JSON.
Why does my JSON work in JavaScript but fail in the validator?
Because you tested a JS object literal ({a:1, b:2,}) in the console, not a JSON string. JS forgives unquoted keys, single quotes, and trailing commas; JSON.parse does not. Generate JSON via JSON.stringify({a:1, b:2}) — it emits strict {"a":1,"b":2} every time.
How do I fix single quotes in JSON?
Replace every ' that wraps a key or string with ", and escape any inner " as \". Example: {'name':'Ada\'s'} → {"name":"Ada's"} (apostrophe inside double quotes is fine). Don’t regex-replace blindly — use a converter in our JSON tools that handles escaping correctly.
Is `undefined`, `NaN`, or `Infinity` valid in JSON?
No — only null is the JSON null. undefined is JS-only, and NaN/Infinity are non-finite numbers forbidden by RFC 8259. Use null or a string sentinel ("NaN") and handle it in code after JSON.parse. See MDN JSON.
How do I validate JSON quickly?
Paste into Toolwasp JSON tools or JSONLint — it shows line/column and highlights the offending token. CLI: python -m json.tool data.json or jq . data.json prints pretty or errors with position. In CI, gate with jq -e . data.json > /dev/null.
What’s the difference between JSON, JSONC, and JSON5?
JSON is strict RFC 8259 (for APIs). JSONC allows comments + trailing commas (VS Code tsconfig.json). JSON5 allows single quotes, unquoted keys, trailing commas, comments (human configs). YAML allows all plus multiline. Use JSON for the wire; use the others for authoring and convert before fetch. See ECMA-404.
Last updated: September 2, 2026 • Author: Toolwasp Team • Sources verified Sep 2, 2026: json.org, RFC 8259, MDN JSON, MDN JSON.parse, MDN JSON bad parse, ECMA-404, JSONLint, Toolwasp JSON tools.