What “valid JSON” actually means
JSON is defined by RFC 8259 and by ECMA-404, which describe the same grammar. It is a small grammar — small enough to print in full — and validity is a purely syntactic question. A document is valid if it is exactly one JSON value, optionally surrounded by whitespace, and nothing else.
A JSON value is one of seven things:
| Value | Written as | Notes |
|---|---|---|
| Object | {"a": 1} | Zero or more "name": value pairs, comma-separated. Names must be strings in double quotes. |
| Array | [1, 2] | Zero or more values, comma-separated. Elements may be of mixed types. |
| String | "text" | Double quotes only. Characters below U+0020 must be escaped. |
| Number | -1.5e3 | Optional minus, integer part, optional fraction, optional exponent. No leading zeros, no +, no hex. |
| true | true | Lowercase. |
| false | false | Lowercase. |
| null | null | Lowercase. |
Whitespace between tokens may only be space (U+0020), horizontal tab (U+0009), line feed (U+000A) or carriage return (U+000D). A non-breaking space pasted from a word processor is not whitespace to a JSON parser; it is an illegal character, and it is invisible, which is why this validator names it explicitly when it finds one.
Things that are valid and surprise people
"hello"on its own is a valid JSON document. So is42,trueandnull. The old RFC 4627 required the top level to be an object or an array; RFC 7159 removed that restriction in 2014 and RFC 8259 kept the change. Some elderly parsers and some strict API gateways still reject a scalar at the top level.{}and[]are valid — empty is fine.{"": 1}is valid. The empty string is a legal key.{"a": 1, "a": 2}is not forbidden. The spec says names should be unique but does not require it. See below for why that is a security problem.1e400is syntactically valid even though it is beyond the range of a double.JSON.parsereturnsInfinityfor it; other parsers differ."\uD800"— a lone high surrogate with no partner — is accepted by the grammar and byJSON.parse, even though the resulting string cannot be encoded as valid UTF-8.
Things that are invalid and surprise people
- A trailing comma anywhere:
[1, 2,],{"a": 1,}. - Comments of any kind:
//,/* */,#. - Single-quoted strings, and unquoted object keys.
NaN,Infinity,-Infinity,undefined.- Numbers written as
.5,5.,+5,0x1F,1_000,007. - A real newline or tab inside a string. They must be
\nand\t. - A byte order mark before the opening brace. The BOM is invisible, entirely legal in a UTF-8 file, and rejected by every conforming JSON parser.
- Two documents concatenated. One file, one value.
How this validator reports errors
The browser’s own JSON.parse decides validity, so the verdict matches
what your code will do. But JSON.parse messages are terse and engine-specific,
so when it refuses, a second pass runs: a strict RFC 8259 scanner that walks the text
looking for the first illegal construct and tries to name it in the terms a developer would
use.
You get four things:
- A named cause. Not “unexpected token” but “Trailing
comma before
}”, “Unquoted property name:port”, “Unescaped line feed (a real newline) inside a string”. - A position. Line, column and raw character index, so it works whether your editor counts lines or offsets.
- The line, with a caret. Long lines are windowed around the caret so that a minified single-line document still produces a readable excerpt.
- What to do about it. A hint aimed at the actual cause — that a
Windows path needs its backslashes doubled, that a zip code beginning with zero has to be
a string, that a document starting with
<is an error page.
The scanner is cross-checked against JSON.parse on twenty thousand randomly
mutated documents. The two agree on every one about whether the input is valid, so the
diagnosis never contradicts the verdict.
Worked examples
1. The error is reported after the mistake
{
"host": "db.internal"
"port": 5432
}
Reported at line 3, column 3 — at "port", not at
the end of line 2 where the comma is missing. That is inherent to how parsers work: after
reading a complete value the parser expects a comma or a closing brace, and it only knows
something is wrong when it sees the next string instead. Whenever a JSON error points at
a line that looks perfectly fine, look at the line above. It is by far the most
common false lead in JSON debugging.
2. The invisible character
{
"name": "Ada"
}
That document looks correct and may well be, but if the space before
"name" was pasted from a PDF or a rich-text email it might be U+00A0, a
non-breaking space. To a JSON parser that is not whitespace at all, and the error
message from most tools is a bewildering “unexpected token” pointing at a
character you cannot see. This validator names it: “Invisible character U+00A0
(a non-breaking space) where a property name was expected”. The same check
covers zero-width spaces, ideographic spaces, curly quotes and a stray BOM in the middle
of a file.
3. A truncated download
{"users":[{"id":1,"name":"Ada"},{"id":2,"na
Reported as “This string is never closed” at the quote that opens
"na. When a document simply stops, the cause is nearly always transport, not
syntax: a response read with a fixed-size buffer, a stream consumed before it finished, a
curl interrupted, a log rotated mid-write, or a database column too narrow
for the value. Check the length of what you received against the
Content-Length header before you go looking for a bug in the producer.
What a syntax validator cannot tell you
Valid JSON is a very low bar. It says nothing about whether the document means what you intended:
- Schema conformance. Whether
ageis a number, whetheremailis present, whetherstatusis one of three allowed strings. That is JSON Schema, enforced by a library such as Ajv (JavaScript),jsonschema(Python) oreverit(Java). - Semantic sanity.
{"temperature_celsius": 4000}is perfectly valid JSON. - Encoding correctness. A document can be valid JSON and still be
mis-decoded on arrival if the producer wrote UTF-8 and the consumer assumed Latin-1. The
symptom is
éwhereébelongs — valid JSON containing wrong text. - Duplicate-key safety. Since duplicates are not forbidden and parsers resolve them differently — JavaScript keeps the last, some keep the first, some error — a crafted payload can be validated by one service and acted on differently by another. If you consume untrusted JSON, reject duplicate keys explicitly rather than relying on the parser.
- Size safety. A deeply nested document can exhaust a recursive parser’s stack; a large one can exhaust memory. Both are valid JSON. Bound the depth and the byte length before parsing untrusted input.
Validating JSON without a browser
For files, a scriptable check is usually better than a web page. All of these run locally and exit non-zero on failure, so they drop straight into a pre-commit hook or a CI job:
# jq — prints the parsed document, or an error with a byte offset
jq empty file.json
# Python, in the standard library, no dependencies
python3 -m json.tool file.json > /dev/null
# Node.js
node -e 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"))' file.json
# Every .json file in a repository, stopping at the first failure
find . -name '*.json' -not -path './node_modules/*' -print0 \
| xargs -0 -n1 -I{} sh -c 'jq empty "{}" || echo "FAILED: {}"'
A web page is the better tool when you have a snippet rather than a file, when you want to see where the error is rather than read an offset, or when you want to explore the shape of an unfamiliar payload afterwards. That is why this page hands you the tree view and the statistics the moment the document validates.
Frequently asked questions
Is a single string or number valid JSON?
Yes, under RFC 7159 (2014) and RFC 8259 (2017), which is what
modern parsers implement. "hello", 42, true and
null are each complete JSON documents. The original RFC 4627 required an
object or array at the top level, so very old parsers and a few strict API gateways still
refuse them. If you are writing a public API, wrapping scalars in an object costs nothing
and avoids the argument.
Why does the error point at a line that looks fine?
Because a parser reports where it noticed the problem, not where you made it. A missing comma is detected at the beginning of the next value; an unclosed brace is detected at the end of the file. When the flagged line looks correct, read the line above it, and check whether a string opened earlier was ever closed.
My JSON is valid in my editor but fails here. Why?
Almost always one of three things. Your editor may be validating JSON5 or JSONC rather than JSON — VS Code does this for files it recognises as settings, which is why comments look fine there. Your file may begin with a byte order mark that the editor hides. Or the copy that reached the clipboard may be a fragment: copying a folded region or a selection that stops at the viewport edge is easy to do without noticing.
Does the validator modify my document?
It does not touch the text in the input box. The formatted and
minified output shown below are newly serialised from the parsed value, which is why they
can differ in whitespace, number formatting and \u escapes. Your original
stays exactly as pasted.
Can I validate a very large file here?
Several megabytes, comfortably. Above about 300 KB the tool
stops re-checking on every keystroke and waits for you to press Format or Minify. Beyond a
few hundred megabytes, no browser page is the right answer — use a streaming parser
such as jq, Python’s ijson or Jackson’s streaming
API, which never hold the whole document in memory.
Is my data sent anywhere for validation?
No. Validation is JSON.parse plus a scanner written in
the JavaScript on this page, both running in your tab. There is no upload endpoint and
nothing is stored. Open the Network panel of your developer tools and press Format —
your document does not appear in any request, because no code here is capable of sending
it.
What is the difference between valid JSON and a valid API response?
Validity is syntax. An API contract is everything else: required
fields, types, enumerations, formats, ranges, and the status code and content type the
response arrived with. A 500 response carrying {"error":"boom"} is perfectly
valid JSON. Validate syntax here, then validate meaning with a JSON Schema in your
tests.
Related pages
- JSON formatterPretty-print, tree view and statistics
- “Unexpected token” explainedEvery cause of the most common JSON error
- “Unexpected token < … position 0”Your API returned HTML, not JSON
- “Unexpected end of JSON input”Empty bodies, truncation and double-reads
- “JSON.parse: unexpected character”The Firefox and Safari wording, decoded
- JSON minifierStrip whitespace and measure the saving
- Escape & unescape stringsJSON string literals, both directions