Skip to content
Control Plane Labs

How JSON.parse actually validates your JSON

The pretty-print button hides a strict spec. Here's what the browser really checks, why some errors are unhelpful, and how to read them anyway.

Control Plane Labs Staff

Published August 5, 2026

Every online JSON formatter — including the one at /tools/json-formatter/ — is a thin wrapper around two ECMAScript built-ins: JSON.parse and JSON.stringify. The pretty-printing is cosmetic. The interesting work is the parse — and the parse follows a spec that is stricter than most people expect.

What “valid JSON” actually means

The current wire spec is RFC 8259, which supersedes RFC 7159 and 4627. Its grammar (§2) allows exactly:

  • Objects — brace-delimited, keys must be double-quoted strings, values separated by commas, no trailing comma after the last pair.
  • Arrays — bracket-delimited, same trailing-comma rule.
  • Strings — double quotes only, backslash escapes, \uXXXX for BMP code points, surrogate pairs for anything above.
  • Numbers — IEEE 754 double-precision, no +, no leading zero except 0 itself, exponents allowed. Not NaN. Not Infinity. Not undefined.
  • Booleans and null — lowercase only.

Everything else is a syntax error. That includes several constructs that show up in JavaScript, YAML, and hand-edited config files every day:

  • { foo: 1 } — unquoted key.
  • { "foo": 1, } — trailing comma.
  • { 'foo': 1 } — single quotes.
  • // comment or /* comment */ — no comments in JSON.
  • NaN, Infinity, -Infinity, undefined — not JSON values.
  • 0123, .5, 1. — malformed numbers.

Why the error messages are frustrating

JSON.parse throws a SyntaxError. The message format is not specified by ECMAScript, so every JavaScript engine writes its own. Chrome (V8) is the most informative — it prints the position and a snippet:

SyntaxError: Unexpected token 'a', "{ "name": alice }" is not valid JSON
    at JSON.parse (<anonymous>)

Firefox (SpiderMonkey) gives a line and column:

SyntaxError: JSON.parse: expected double-quoted property name at line 1 column 12 of the JSON data

Safari (JavaScriptCore) is the terse one:

SyntaxError: JSON Parse error: Expected '"'

The tool renders whichever your browser produces — that’s the best signal available, because ECMA-262 §25.5.1.1 refuses to standardize the wording.

When the message is unhelpful (“Unexpected end of JSON input”), the trick is to bisect: cut the input in half, format each half, and the half that still errors contains the problem. Two or three bisections usually pinpoint it.

The four errors that account for most real inputs

1. Trailing commas from JavaScript source. You copied { "foo": 1, } out of a .js file. JavaScript object literals allow it since ES2017; JSON never has. Delete the comma.

2. Single quotes from a shell paste. The command line quoted the whole payload in double quotes, so the inner strings ended up in single quotes. Wrap once more with a JSON quote-fixing step, or paste the raw payload from your terminal’s captured output rather than the escaped one.

3. Unquoted keys from Python dict.__repr__ or YAML. Python str(some_dict) produces {'a': 1} — that’s Python literal syntax, not JSON. Use json.dumps on the Python side, or paste through a YAML→JSON conversion (which happens to accept unquoted keys as YAML, then emits valid JSON).

4. Comments. Someone added // TODO to a config file. JSON forbids it; JSONC (used by VS Code and TypeScript) permits it. The formatter treats input as strict JSON. Strip the comments, or use a JSONC-aware tool.

Pretty-printing vs. re-serializing

The tool uses JSON.stringify(JSON.parse(input), null, indent). That means the output is a new serialization, not a reformat of the input string. Two consequences worth knowing:

  • Key order is preserved for string keys and follows the object’s own insertion order (ECMAScript §7.3.23), which matches what every current engine ships. If you rely on key ordering, this is stable.
  • Numeric precision is IEEE 754. 0.1 + 0.2 becomes 0.30000000000000004, and 9999999999999999 becomes 10000000000000000. If your payload uses large integers or fixed-point decimals, use string encoding on the producer side. JSON.parse cannot round-trip a 64-bit integer through a JS number.

When JSON.parse is the wrong tool

  • Streaming inputJSON.parse requires the full payload in memory. For multi-GB inputs, use a streaming parser (clarinet, stream-json) instead of a formatter.
  • JSONL / ND-JSON — one JSON value per line. The formatter as written reads the whole textarea as a single value and will throw on the second brace. Split by newline first.
  • Comments and trailing commas — use JSONC or JSON5. Both are supersets; neither is what JSON.parse accepts.

FAQ

Frequently asked questions

Is my input sent to a server?+
No. The formatter runs entirely in your browser using JSON.parse and JSON.stringify. The URL query string is used only to make the current state shareable — the input never leaves your machine unless you copy the URL somewhere.
Why does my file parse in VS Code but not here?+
VS Code and most editors default to JSONC (JSON with Comments), which permits // and /* */ comments and trailing commas. This tool follows strict RFC 8259 because that is what wire protocols and APIs accept. Strip comments and trailing commas before parsing.
Can I trust the output ordering?+
For string keys, yes — modern engines preserve insertion order and re-emit in that order (ECMAScript §7.3.23). Integer-shaped keys ('0', '1', '2') are placed first, in numeric order, which matches every current browser and Node.js runtime.
Why is my large integer being corrupted?+
JavaScript numbers are IEEE 754 doubles. Integers above 2^53 lose precision. JSON.parse cannot preserve them. The fix is on the producer side — encode as a string ('1234567890123456789') and parse to BigInt in code that needs the exact value.

Tags: #json, #rfc-8259, #web-dev