JSON is the simplest data format in common use, and developers still lose hours to it every week. The reason is a mismatch: JSON's grammar is strict, but the tools that produce it, hand edits, string concatenation, log pipelines, LLM output, copy-paste from chat apps, are sloppy. This guide covers the syntax rules that actually trip people up, the error messages worth memorizing, and a debugging workflow that turns "invalid JSON" from a twenty-minute hunt into a twenty-second fix.
The grammar, in one minute
A JSON document is one value. A value is an object, array, string, number, true, false, or null. Objects are brace-wrapped collections of "key": value pairs separated by commas; keys must be double-quoted strings. Arrays are bracket-wrapped, comma-separated values. That is the whole language. Everything that breaks comes from the details:
- Strings use double quotes only. Single quotes are valid in JavaScript, Python, and SQL, and invalid in JSON. This is the number one error from hand-written JSON.
- No trailing commas.
[1, 2, 3,]is fine in modern JavaScript and a parse error in JSON. The number two error, and the most common one introduced by deleting the last line of a config block. - No comments. Not
//, not/* */, not#. If a config format accepts comments (tsconfig, VS Code settings), it is JSONC, a superset, and pasting it into a strict parser fails. - Numbers are strict. No leading zeros (
012fails), no bare decimals (.5fails, write0.5), noNaNorInfinity, no hex.1e10scientific notation is fine. - Specific escape sequences only. Inside strings:
\"\\\/\b\f\n\r\tand\uXXXX. A lone backslash, as in a Windows path"C:\Users\me", is invalid; it must be"C:\\Users\\me". - Keys must be quoted.
{name: "x"}is a JavaScript object literal, not JSON.
Decoding the error messages
Parsers report errors at the position where parsing became impossible, which is often after the actual mistake. Knowing the patterns saves real time:
"Unexpected token ' in JSON at position N"
Single quotes. The position points at the offending quote. Fix all of them, not just the first; they travel in pairs and packs.
"Unexpected token } " or "Unexpected token ]"
Nearly always a trailing comma on the line above the reported position. The parser saw the comma, expected another value, and found a closing bracket instead.
"Unexpected end of JSON input"
The document was truncated: an unclosed brace or bracket, or, very commonly, a string that was cut off mid-stream. Check the last line first, then count braces. Truncated API responses and clipped log lines are the usual suspects.
"Unexpected token o in JSON at position 1"
A JavaScript classic: you called JSON.parse on something that is already an object, so it stringified to [object Object] and the parser choked on the o. The bug is in your code, not your data.
"Invalid escape" or "Bad escaped character"
Unescaped backslashes, usually Windows paths or regex patterns stored as strings.
A debugging workflow that scales
For a 10-line payload you can eyeball it. For a 5 MB API response you need a process:
- Step 1: Pretty-print it. Minified JSON puts everything at position 48211 on line 1. Formatting it first converts an opaque offset into a line number you can jump to. A good formatter with a validator built in does both in one paste and points you at the failing line.
- Step 2: Read the line above the error. Trailing commas and missing commas both manifest one token later than they occur.
- Step 3: Binary-search big documents. If the error position is unhelpful, delete the bottom half of the document and close the brackets. Still failing? The error is in the top half. Repeat. Four or five iterations isolates almost anything.
- Step 4: Suspect the transport, not the JSON. Smart quotes from a chat app or word processor (
“instead of"), non-breaking spaces, BOM characters at the start of a file, and HTML-encoded entities (") all produce JSON that looks perfect and parses as garbage. If the document looks right and still fails, hex-dump the first few bytes and the area around the error.
Validation beyond syntax: JSON Schema
Syntactic validity says the document parses. It says nothing about whether age is a number, email is present, or status is one of three allowed values. That is schema validation's job:
{
"type": "object",
"required": ["email", "plan"],
"properties": {
"email": { "type": "string", "format": "email" },
"plan": { "enum": ["free", "pro"] },
"seats": { "type": "integer", "minimum": 1 }
}
}
Validate at trust boundaries: API request bodies, webhook payloads, configuration files at startup, and anything an LLM produced. Libraries exist for every stack (Ajv for Node, jsonschema for Python, opis/json-schema for PHP). The startup-config case is underrated; a service that validates its config at boot fails loudly at deploy time instead of mysteriously at 3 a.m.
Formatting conventions worth standardizing
Formatting is not just aesthetics; it is diff quality. Teams that standardize on two-space indentation, one key per line, and sorted keys where order is not meaningful get version-control diffs that show actual changes instead of reflow noise. Two practical notes:
- Minify for transport, format for humans. Whitespace in a 2 MB API response is real bandwidth on mobile. Serve minified; format at the point of reading.
- Beware key order assumptions. JSON objects are unordered by specification. Most parsers preserve insertion order in practice, and code that silently depends on it breaks when a different serializer gets involved.
Common pitfalls that are not syntax errors
- Big integers. JavaScript numbers lose precision above 2^53 - 1. A 64-bit database ID like
9007199254740993survives parsing but comes out wrong. APIs that matter ship IDs as strings for exactly this reason. - Duplicate keys.
{"a": 1, "a": 2}is technically parseable and most parsers silently keep the last value. Some security scanners flag this because different parsers disagreeing on the winner is an attack vector. - Encoding. JSON must be UTF-8 on the wire (RFC 8259). A Latin-1 encoded file with accented characters will fail or mangle. If you see
éstyle escapes, that is fine and valid; if you see literal mojibake, fix the encoding upstream. - Dates. JSON has no date type. Standardize on ISO 8601 strings (
"2026-06-09T12:00:00Z") and convert at the edges. Epoch integers work too; just pick one. Our timestamp converter helps when you are staring at1780000000wondering what day that is.
JSON5, JSONC, and the lenient cousins
Part of the confusion around JSON syntax is that several supersets exist and tools quietly accept them. JSONC (JSON with Comments) adds // and /* */ comments and tolerates trailing commas; VS Code's settings and tsconfig.json use it. JSON5 goes further: unquoted keys, single quotes, hex numbers, multi-line strings. Both are fine inside the ecosystems that chose them, and both produce documents that fail in any strict parser, which is every API on earth. The rule that keeps you safe: know which dialect each file is, and when data crosses a system boundary, it must be strict RFC 8259 JSON. If you write configs in a lenient dialect, convert before shipping the value anywhere else.
The fast path
Every technique in this guide gets faster with a tool that formats, validates, and pinpoints errors in one step. Our free JSON Formatter and Validator does exactly that: paste anything, get either clean formatted output or an error message with the line position, plus one-click minification for the transport direction. It runs entirely in your browser, so API responses with real customer data never leave your machine. And if you are debugging a JSON Web Token rather than a payload, the JWT Decoder unpacks header and claims the same privacy-safe way. For the markup equivalent of this guide, see our piece on HTML minification, where the same minify-for-transport, format-for-humans logic applies.


