JSON is everywhere a program needs to hand data to another program: API responses, config files, log lines, message queues. It is small enough to learn in an afternoon, which is exactly why people skip learning it properly and then spend an hour staring at

text
Unexpected token } in JSON at position 142
. This guide covers the full syntax, the errors that actually happen in practice, and how to read the parser when it complains.

What JSON Actually Is

JSON (JavaScript Object Notation) is a text format for structured data. It looks like a subset of JavaScript object literals, but it is a strict, language-independent standard. Parsers in Python, Go, Rust, and Java all expect the same exact rules. The grammar is deliberately tiny, and most bugs come from treating it as if it were JavaScript, YAML, or a Python

text
dict
.

A valid JSON document is exactly one value. That value is usually an object or an array, but a bare string, number,

text
true
,
text
false
, or
text
null
is also valid JSON on its own.
text
42
is a complete JSON document. So is
text
"hello"
.

The Six Value Types

JSON has exactly six types and no more:

  • Object — an unordered set of key/value pairs wrapped in
    text
    { }
  • Array — an ordered list wrapped in
    text
    [ ]
  • String — text in double quotes:
    text
    "text"
  • Number
    text
    42
    ,
    text
    -3.14
    ,
    text
    6.022e23
    (no leading
    text
    +
    , no leading zeros, no
    text
    NaN
    , no
    text
    Infinity
    )
  • Boolean
    text
    true
    or
    text
    false
    , lowercase only
  • Null
    text
    null
    , lowercase only

That is the entire type system. There is no date type, no integer-vs-float distinction at the spec level, no comments, and no trailing commas. Anything outside this list is not JSON, even if your editor highlights it nicely.

Objects

An object is zero or more key/value pairs. Keys must be strings in double quotes. Values can be any of the six types, including nested objects and arrays.

json
{ "name": "Ada", "active": true, "projects": 3, "manager": null, "address": { "city": "London", "zip": "SW1A" } }

Keys are not required to be unique by the spec, but if you repeat a key most parsers keep the last one and silently drop the rest — a quiet source of bugs. Treat keys as unique.

Arrays

An array is an ordered list of values. The values do not have to share a type, though keeping them homogeneous makes the data far easier to consume.

json
{ "tags": ["data", "json", "parsing"], "scores": [98, 87.5, 100], "mixed": [1, "two", true, null] }

Empty objects

text
{}
and empty arrays
text
[]
are both valid and common.

The Errors You Will Actually Hit

Nearly every JSON parse failure is one of a handful of mistakes. Here they are, with the fix for each.

Trailing Commas

This is the single most common JSON error, because JavaScript, Python, and most modern languages allow trailing commas in their own literals. JSON does not. A comma separates items; it must be followed by another item, never by a closing bracket.

json
{ "a": 1, "b": 2, }

The comma after

text
2
is illegal. Fixed:

json
{ "a": 1, "b": 2 }

The same applies inside arrays —

text
[1, 2, 3,]
is invalid; drop the last comma. If you are generating JSON by concatenating strings in a loop, this is the bug you will create. Build a list and serialize it with a real JSON library instead of gluing commas by hand.

Single Quotes

JSON strings and keys must use double quotes. Single quotes are a JavaScript and Python habit that the JSON grammar simply does not include.

json
{ 'name': 'Ada' }

Fixed:

json
{ "name": "Ada" }

If your text contains a double quote, escape it with a backslash:

text
"She said \"hi\""
. You do not switch to single quotes to avoid escaping — there is no single-quote option in JSON.

Unquoted Keys

In JavaScript you can write

text
{ name: "Ada" }
. In JSON every key must be a quoted string.

json
{ name: "Ada", active: true }

Fixed:

json
{ "name": "Ada", "active": true }

This one is common when someone copies an object literal out of source code and tries to use it as a config file. Quote every key.

Comments

JSON has no comment syntax. Not

text
//
, not
text
/* */
, not
text
#
. This is a frequent surprise in config files, where developers reach for comments out of habit.

json
{ // database connection "host": "localhost", "port": 5432 }

The

text
//
line breaks the parse. Fixed — move the note out of the data, or fold it into a real field:

json
{ "_comment": "database connection", "host": "localhost", "port": 5432 }

A

text
_comment
key is a pragmatic convention, not part of the spec, but it parses cleanly and is ignored by your code. If you genuinely need comments in a config format, JSON is the wrong choice — that is what JSON5, JSONC, or YAML exist for. Just do not feed those to a strict JSON parser.

Wrong Literals for null and Booleans

text
null
,
text
true
, and
text
false
are lowercase keywords.
text
NULL
,
text
None
,
text
True
,
text
False
, and
text
nil
are all invalid.

json
{ "manager": None, "active": True }

Fixed:

json
{ "manager": null, "active": true }

This is the classic mistake when someone pastes a Python

text
repr()
and calls it JSON. Python's
text
dict
print form and JSON look similar but are not interchangeable.

Number Formatting

JSON numbers are stricter than they look. No leading zeros (

text
017
is invalid), no leading
text
+
, no trailing decimal point, and no hex.
text
NaN
and
text
Infinity
are not valid JSON values either, even though JavaScript produces them —
text
JSON.stringify(NaN)
returns the string
text
"null"
for exactly this reason.

json
{ "a": 01, "b": +5, "c": 3. }

Fixed:

json
{ "a": 1, "b": 5, "c": 3.0 }

Unescaped Control Characters

A literal newline or tab inside a string must be escaped as

text
\n
or
text
\t
. A raw line break in the middle of a quoted string is invalid.

json
{ "note": "line one line two" }

Fixed:

json
{ "note": "line one\nline two" }

How to Read a Parser Error Message

The error text looks cryptic but almost always points you straight at the problem. Two pieces of information matter: the position and the token.

Position. Messages like

text
at position 142
or
text
line 7 column 3
tell you where the parser gave up — which is usually one character after the real mistake. A trailing comma error reports the position of the closing bracket, because that is the point where the parser expected another value and instead found
text
}
. So when you see a position, look at it and at the character just before it.

Token.

text
Unexpected token } in JSON
means the parser hit
text
}
when it wanted something else — almost always a trailing comma earlier.
text
Unexpected string
or
text
Expected ',' or '}'
usually means a missing comma between two pairs.
text
Unexpected end of JSON input
means the document was cut off — an unclosed bracket, brace, or string somewhere above.

Here is the mental translation table:

  • text
    Unexpected token }
    /
    text
    ]
    → trailing comma right before it
  • text
    Unexpected token
    on a key → unquoted key or single quotes
  • text
    Expected ',' or '}'
    → you forgot a comma between two pairs
  • text
    Unexpected end of input
    → an unclosed
    text
    {
    ,
    text
    [
    , or
    text
    "
  • text
    Unexpected non-whitespace character after JSON
    → two values where one is allowed, or a stray character at the end

The line/column numbers from compiled-language parsers (Go, Rust, Java) tend to be more precise than the byte-offset numbers from older JavaScript engines, but both point to roughly the same spot. Trust the location, then scan upward a line or two.

A Workflow That Catches These Fast

Reading positions by hand works for a 10-line file. For a 4,000-line API response it does not. A few habits make JSON debugging almost mechanical:

  1. Format before you read. Minified JSON hides structure. Pretty-printing it with consistent indentation makes a missing bracket or stray comma jump out visually. Our JSON formatter does this in the browser and reports the first parse error with its location, which is faster than squinting at a one-line blob. It runs entirely client-side, so you can paste data you would not want to send to a server.
  2. Validate, do not eyeball. A validator confirms the whole document parses, not just the part you are looking at. Fix the first reported error, re-validate, repeat — errors often cascade, and the second one only appears once the first is gone.
  3. Generate, do not hand-write. If your code emits JSON, use the language's serializer (
    text
    json.dumps
    ,
    text
    JSON.stringify
    ,
    text
    encoding/json
    ) rather than building strings. Serializers cannot produce trailing commas, unquoted keys, or single quotes.
  4. Convert when you need a different shape. Once your JSON is valid, you often want it somewhere else — a spreadsheet, a database import, a quick scan in a table. Converting an array of objects to a flat table with a JSON to CSV tool is usually faster than writing a one-off script, and it surfaces inconsistent keys across records as a side effect.

The Short Version

JSON is strict on purpose — that strictness is what lets every language agree on what the data means. Memorize the six types, use double quotes everywhere, never leave a trailing comma, never add comments, and keep

text
null
/
text
true
/
text
false
lowercase. When the parser complains, read the position and the token name; they tell you what it wanted and where it stopped looking. Format the document first, fix the top error, and re-check. Do that and JSON stops being a source of mystery failures and goes back to being the boring, reliable format it was meant to be.