Working with JSON feels like it should be simple. It is a lightweight, human-readable data format used everywhere from API responses to local configuration files. Yet, because the specification (RFC 8259) is completely unforgiving, developers spend an enormous amount of time untangling syntax exceptions.
This guide breaks down the 10 most common JSON errors developers encounter in the wild, the exact parser error messages they trigger, why parsers reject them, and how to fix them with clear before-and-after examples. Every error below was actually run through two real parsers — Node.js v24 (JSON.parse) and Python 3.12 (json.loads) — and the verbatim messages are shown. That matters, because several of the error messages quoted in the original version of this article were from older versions of V8 and no longer match what a modern parser prints.
Identifying and resolving common JSON syntax errors in development
Tested: All 10 Errors, Two Parsers, Real Messages
Here is the scoreboard. Each invalid input was fed to both parsers. Nine of the ten fail in both; one — duplicate keys — parses fine in both, silently. Note the pattern: the two parsers report the same position for every error (character 58 for the trailing comma, character 4 for the single quotes), but word the message differently. Learn to read the position first and the wording second.
| Error | Node.js v24 (JSON.parse) | Python 3.12 (json.loads) |
|---|---|---|
| 1. Trailing comma | Expected double-quoted property name in JSON at position 58 (line 5 column 1) | Expecting property name enclosed in double quotes: line 5 column 1 (char 58) |
| 2. Single quotes | Expected property name or '}' in JSON at position 4 (line 2 column 3) | Expecting property name enclosed in double quotes: line 2 column 3 (char 4) |
| 3. Unquoted keys | Expected property name or '}' in JSON at position 4 (line 2 column 3) | Expecting property name enclosed in double quotes: line 2 column 3 (char 4) |
| 4. Comments | Expected property name or '}' in JSON at position 4 (line 2 column 3) | Expecting property name enclosed in double quotes: line 2 column 3 (char 4) |
| 5. Unescaped quotes | Expected ',' or '}' after property value in JSON at position 27 (line 2 column 26) | Expecting ',' delimiter: line 2 column 26 (char 27) |
| 5b. Literal newline in a string | Bad control character in string literal in JSON at position 24 (line 2 column 23) | Invalid control character at: line 2 column 23 (char 24) |
| 6. Missing closing brace | Expected ',' or '}' after property value in JSON at position 40 (line 4 column 4) | Expecting ',' delimiter: line 4 column 4 (char 40) |
7. Function / undefined | Unexpected token 'u', ..."llback": function() "... is not valid JSON | Expecting value: line 2 column 15 (char 16) |
| 8. Leading zero | Unexpected number in JSON at position 13 (line 2 column 12) | Expecting ',' delimiter: line 2 column 12 (char 13) |
| 9. Bad unicode escape | Bad Unicode escape in JSON at position 20 (line 2 column 19) | Invalid \uXXXX escape: line 2 column 15 (char 16) |
| 10. Duplicate keys | Parses OK — {"timeout": 5000} (last wins, silently) | Parses OK — {'timeout': 5000} (last wins, silently) |
One correction to the old version of this article: its diagnostic table quoted messages like “Unexpected token } in JSON at position…” and “Unexpected token ‘ in JSON at position…”. Those are older V8 wordings. Modern Node.js v24 does not print them — errors 1–4 above all start with “Expected…” instead. If you paste an old error message into a search engine and find nothing, this is usually why: the parser’s wording changed, but the position it reports is the same.
Quick Diagnostic Reference Table
If your build, API request, or data ingestion pipeline just threw a JSON syntax exception, use this reference table to find the matching fix. The messages shown are the modern Node.js v24 wordings, verified by running each input:
| Error Symptom / Pattern | Modern Parser Error Message (Node.js v24) | Root Cause |
|---|---|---|
| Trailing Comma | Expected double-quoted property name in JSON at position... | A comma left at the end of the final item in an object or array |
| Single Quotes | Expected property name or '}' in JSON at position... | Using single quotes instead of mandatory double quotes |
| Unquoted Keys | Expected property name or '}' in JSON at position... | Omitting double quotes around object property names |
| Comments | Expected property name or '}' in JSON at position... | Adding // or /* */ notes inside standard JSON files |
| Unescaped Quotes/Newlines | Expected ',' or '}' after property value... / Bad control character in string literal... | Raw control characters or unescaped quotes inside strings |
| Truncated Input | Expected ',' or '}' after property value... (only empty input gives "Unexpected end of JSON input") | Missing closing brackets, braces, or interrupted data streams |
| Unsupported Types | Unexpected token 'u'... is not valid JSON | Passing undefined, functions, or invalid serialization values |
| Leading Zeros | Unexpected number in JSON at position... | Writing numbers with forbidden leading zeros like 0123 |
| Invalid Unicode Escapes | Bad Unicode escape in JSON at position... | Malformed hexadecimal escape sequences failing \uXXXX rules |
| Duplicate Keys | No error — silent overwrite, last value wins | Repeating identical property keys within the same object scope |
1. Trailing Commas
In JavaScript object literals, trailing commas are completely legal. In standard JSON, they are strictly forbidden. Leaving a comma after the final key-value pair of an object or array triggers a parsing failure.
Invalid JSON:
{
"host": "localhost",
"port": 3306,
"debug": true,
}
What the parsers actually said:
Node: SyntaxError: Expected double-quoted property name in JSON at position 58 (line 5 column 1)
Python: JSONDecodeError: Expecting property name enclosed in double quotes: line 5 column 1 (char 58)
Why it fails: The parser expects another key-value pair after the comma, but hits a closing curly brace instead. The position points at the } on line 5 — exactly where the problem is.
Correct JSON:
{
"host": "localhost",
"port": 3306,
"debug": true
}
2. Single Quotes Instead of Double Quotes
Developers who switch back and forth between writing code and writing data often slip into using single quotes for strings or object keys. JSON explicitly mandates double quotes.
Invalid JSON:
{
'status': 'active'
}
What the parsers actually said:
Node: SyntaxError: Expected property name or '}' in JSON at position 4 (line 2 column 3)
Python: JSONDecodeError: Expecting property name enclosed in double quotes: line 2 column 3 (char 4)
Why it fails: The JSON specification (RFC 8259) dictates that JSON strings must be enclosed in double quotation marks, including object property names. Note that modern Node does not say “Unexpected token ‘” — that was the old V8 wording. The position (character 4, the first single quote) is the same either way.
Correct JSON:
{
"status": "active"
}
3. Unquoted Property Keys
In JavaScript, object properties don’t need quotes if they follow standard identifier naming rules. In JSON, property names must always be wrapped in double quotes, or the parser will throw an error.
Invalid JSON:
{
name: "Kingstools",
version: 1.0
}
What the parsers actually said:
Node: SyntaxError: Expected property name or '}' in JSON at position 4 (line 2 column 3)
Python: JSONDecodeError: Expecting property name enclosed in double quotes: line 2 column 3 (char 4)
Why it fails: Standard parsers require explicit string boundaries on property keys before evaluating the colon separator. Notice this is the identical error to single quotes — same message, same position — because to the parser both cases are the same failure: it expected a quoted property name and found something else. When two different mistakes produce the same message, the position is what tells you which one it is.
Correct JSON:
{
"name": "Kingstools",
"version": 1.0
}
4. Embedded Comments (// or /* */)
Configuration formats like JSON5, YAML, or TypeScript support inline and block comments. Standard JSON has no comment specification. If you add notes, parsers encounter the forward slash and throw an error.
Invalid JSON:
{
// Database configuration
"timeout": 5000
}
What the parsers actually said:
Node: SyntaxError: Expected property name or '}' in JSON at position 4 (line 2 column 3)
Python: JSONDecodeError: Expecting property name enclosed in double quotes: line 2 column 3 (char 4)
Why it fails: Forward slashes are not valid token starters at the root or property level in core JSON data. Again the same message and position as errors 2 and 3 — the parser doesn’t care what invalid character it found, only that a quoted property name wasn’t there.
Correct JSON:
{
"timeout": 5000
}
If you genuinely need comments in a config file, use JSON5 or YAML for that file — or keep the notes outside the JSON, in your documentation. Don’t hand-edit comments into a file a strict parser will read.
5. Unescaped Control Characters and Special Quotes
If a string value contains a literal line break, a tab character, or an unescaped double quote inside it, the parser loses its place and misinterprets the syntax boundaries. This one error is really two: unescaped quotes and raw control characters give different messages.
Invalid JSON (unescaped quotes):
{
"message": "User said "Hello" and left."
}
What the parsers actually said:
Node: SyntaxError: Expected ',' or '}' after property value in JSON at position 27 (line 2 column 26)
Python: JSONDecodeError: Expecting ',' delimiter: line 2 column 26 (char 27)
Why it fails: The internal unescaped quotes prematurely close the string value token from the parser’s perspective. The parser reads "User said " as the complete value, then finds Hello where it expected a comma or a closing brace.
Invalid JSON (literal newline inside a string):
{
"message": "line one
line two"
}
What the parsers actually said:
Node: SyntaxError: Bad control character in string literal in JSON at position 24 (line 2 column 23)
Python: JSONDecodeError: Invalid control character at: line 2 column 23 (char 24)
Correct JSON:
{
"message": "User said \"Hello\" and left."
}
{
"message": "line one\nline two"
}
Escape the quote with a backslash, and write the newline as the two characters \n, not as a real line break. This is the error most often caused by hand-building JSON with string concatenation instead of a serializer — which is why the prevention section below says never to do that.
6. Missing Closing Brackets or Braces
In large, deeply nested JSON files, it is remarkably easy to mismatch a closing curly brace (}) or square bracket (]). The old version of this article said this triggers an “Unexpected end of JSON input” exception. That is wrong for this input — we ran it, and neither parser says that.
Invalid JSON:
{
"data": {
"items": [1, 2, 3]
}
What the parsers actually said:
Node: SyntaxError: Expected ',' or '}' after property value in JSON at position 40 (line 4 column 4)
Python: JSONDecodeError: Expecting ',' delimiter: line 4 column 4 (char 40)
Why it fails: The input stream terminates before all open structural scopes are closed — but the parser reports it as an expected-delimiter error at the end of the input, not as “Unexpected end of JSON input”. That classic message appears only when the input is empty (or whitespace-only). Truncated payloads get position-specific errors instead, as we showed in detail in our guide to fixing unexpected end of JSON input — where a cut-off object reported “position 29” and a cut-off string reported “Unterminated string”, never the classic message.
Correct JSON:
{
"data": {
"items": [1, 2, 3]
}
}
The Fix: Append the missing closing curly brace, or test your structure using a JSON formatter and validator to quickly expose structural imbalances.
7. Unsupported Data Types (NaN, undefined, Functions)
Because JSON is a language-independent interchange format, it doesn’t understand JavaScript-specific constructs like undefined, functions, or symbol declarations.
Invalid JSON:
{
"callback": function() {},
"value": undefined
}
What the parsers actually said:
Node: SyntaxError: Unexpected token 'u', ..."llback": function() "... is not valid JSON
Python: JSONDecodeError: Expecting value: line 2 column 15 (char 16)
Why it fails: Functions and undefined are executable code or language states, not storable data types in the JSON specification. The Node error is worth reading closely: it complains about the token 'u', because the parser saw the f of function, tried to read the literal false, and died on the second letter. The error names the letter it choked on, not the word you wrote.
Note the difference between parsing and stringifying here. A strict parser rejects undefined outright — but JavaScript’s own JSON.stringify() handles it silently. We ran it:
JSON.stringify({a: NaN, b: Infinity, c: undefined, d: function(){}, e: "ok"})
// {"a":null,"b":null,"e":"ok"}
NaN and Infinity become null; undefined and function values are dropped from objects entirely; and JSON.stringify(undefined) on its own returns undefined, not the string "undefined". So the danger is silent data loss, not an error — a field your code set simply isn’t in the output, and nothing tells you.
Correct JSON:
{
"callback": null,
"value": null
}
8. Leading Zeros on Numbers
JSON’s number grammar does not allow numbers to have leading zeros (such as 0123), triggering an error. This is the one error message the old version of this article quoted correctly.
Invalid JSON:
{
"code": 0123
}
What the parsers actually said:
Node: SyntaxError: Unexpected number in JSON at position 13 (line 2 column 12)
Python: JSONDecodeError: Expecting ',' delimiter: line 2 column 12 (char 13)
Why it fails: The number production rules in RFC 8259 explicitly prohibit non-zero numbers from starting with a zero digit. Python’s message is less helpful here — “Expecting ‘,’ delimiter” at the same position — because it parsed 0 as a complete number and then didn’t know what to do with 123.
Correct JSON:
{
"code": 123
}
The Fix: Remove unnecessary leading zeros, or wrap the value in double quotes to treat it as a string if the leading zero is intentionally required (like a postal code or ID prefix): {"code": "0123"} parses cleanly — we verified it.
9. Invalid Escaped Unicode Sequences
Unicode escape sequences represent specific Unicode code points within string data. They must strictly follow the exact four-digit hexadecimal format (\uXXXX). Writing sequences with fewer digits, such as \u123, fails structural evaluation.
Invalid JSON:
{
"symbol": "\u123"
}
What the parsers actually said:
Node: SyntaxError: Bad Unicode escape in JSON at position 20 (line 2 column 19)
Python: JSONDecodeError: Invalid \uXXXX escape: line 2 column 15 (char 16)
Why it fails: The parser expects precisely four hex characters following the \u prefix to resolve the code point. Note the two parsers disagree on where the error is for once — Node points at the closing quote (position 20), Python at the escape itself (char 16). Same cause, different finger-pointing.
Correct JSON:
{
"symbol": "\u0123"
}
10. Duplicate Object Keys
RFC 8259 states that object names SHOULD be unique to ensure maximum portability and predictable behavior across different parsers. While some lenient parsers might silently overwrite earlier values with later ones, others throw exceptions or handle state inconsistently.
Invalid/Ambiguous JSON:
{
"timeout": 1000,
"timeout": 5000
}
What the parsers actually said:
Node: PARSED OK -> {"timeout":5000}
Python: PARSED OK -> {'timeout': 5000}
Why it matters: Neither parser errors — both silently keep the last value and discard the first. There is no warning, no error, no trace that 1000 ever existed. That makes this the most dangerous error on this list: the other nine all stop you with an exception, while this one lets a wrong config sail through. It’s also the error behind the “duplicate key” lint flag in our guide to linting vs validating JSON, where a linter catches what the parser happily accepts.
Correct JSON:
{
"timeout": 5000
}
The Fix: Audit your data generation logic to ensure keys are distinct across every object scope. If two systems merge configs, the merge step — not the parser — is where duplicates must be caught.
How to Prevent JSON Errors in Production
Chasing down syntax errors manually is a waste of engineering time. Protect your systems by adopting these best practices:
- Never write JSON by hand: Always rely on built-in serializers like
JSON.stringify()orjson_encode(). Hand-built JSON is where errors 2, 3, 5, and 8 come from — a serializer cannot produce them. - Automate validation: Run automated checks in your CI/CD pipelines. Our guide to JSON validation in CI/CD shows
jqandajvchecks that fail a pull request on a trailing comma (exit code 5) before it reaches production. - Validate before you query or send: Paste payloads into our JSON formatter and validator before running a query against them or sending them to an API — a single missing comma breaks the parser before anything else runs, and a validator points at the exact position.
- Lint as well as parse: A parser accepts duplicate keys silently (error 10 above). A linter flags them. Run both — see lint vs validate for the difference.
- Fix broken payloads with a repair step, not by hand: When a payload arrives already damaged — truncated, or served as HTML instead of JSON — our guide to repairing broken JSON walks through diagnosing it from the actual error message.
Frequently Asked Questions
Why does my error message not match the one in the docs?
Because parser wording changes between versions. The old V8 wording was “Unexpected token } in JSON at position…”; modern Node.js v24 says “Expected double-quoted property name in JSON at position…”. The position number is stable across versions and across parsers — trust the position, not the phrasing.
Why do single quotes, unquoted keys, and comments all give the same error?
Because to the parser they are the same failure: it expected a double-quoted property name at that position and found something else. Node reports “Expected property name or ‘}'” for all three, at the same character position. Use the position to find the spot, then look at what character is actually there.
Why does a missing closing brace not say “Unexpected end of JSON input”?
Because that message is reserved for empty (or whitespace-only) input in modern Node. A truncated object or array gets a position-specific error instead — in our run, “Expected ‘,’ or ‘}’ after property value in JSON at position 40”. See our unexpected end of JSON input guide for the full breakdown of which inputs give which message.
Are duplicate keys an error or not?
Not to the parser — both Node and Python silently keep the last value, as we ran above. They are an error to a linter and to RFC 8259’s SHOULD, which says names should be unique. That’s why you need a lint step in addition to parsing: the parser will never tell you a value was silently overwritten.
How do I find the exact spot an error points to?
Take the position number from the error (for example, “position 58”) and count characters from the start of the input, or use the line and column the parser gives you (line 5, column 1). Pasting the payload into a JSON formatter and validator highlights the spot for you — far faster than counting by hand in a large file.
Summary
We ran all 10 of these errors through Node.js v24 and Python 3.12, and the results corrected three things the old version of this article got wrong: modern Node no longer says “Unexpected token…” for most of these errors (it says “Expected…”), a missing closing brace does not produce “Unexpected end of JSON input”, and duplicate keys are the one error no parser will ever report — both parsers silently kept the last value. The fixes are simple once you know the cause: drop the trailing comma, use double quotes, quote your keys, remove comments, escape quotes and newlines, close your braces, use null instead of undefined, drop leading zeros, write four hex digits after \u, and never repeat a key. And the cheapest fix of all is prevention: serialize with a library, validate in CI, and lint as well as parse — then these ten errors stop reaching production in the first place.

11 Comments