Tools
Guides
On this page

August 9, 2026

JSON formatting and validation in practice: pinpoint the error, not the vibes

Every developer has pasted a JSON blob into a parser, been told Unexpected token something, and stared at a wall of text trying to spot the problem. The message rarely says what is wrong in plain language — but it almost always says where, and that is enough. This guide is about turning that “where” into a two-minute fix: why formatting matters, which errors you will actually encounter, and how to read an error location by row and column instead of guessing.

Everything here can be practiced directly on this site. The JSON formatter and validator parses your input in the browser and reports errors with a one-based row and column, and the JSONPath query tool lets you pull specific values out of a document once it parses. Nothing you paste leaves your machine, which matters when the JSON you are debugging is a production request body.

Why formatted JSON earns its keep#

A machine does not care what JSON looks like. {"a":1} and a 40-line pretty-printed version of the same object are byte-for-byte equivalent after parsing — semantically identical, equally valid. Formatting is for humans, and it pays off in three specific ways.

Diffing. Source control compares files line by line. A minified 5,000-character single-line JSON file means every change produces a one-line diff that is unreadable; the same document formatted with two-space indentation produces a small, reviewable diff. If a config file or API fixture lives in a repository, committed formatting is not cosmetic — it is what makes code review possible.

Structure visibility. Indentation makes nesting visible. When an object lands inside the wrong array, or a closing brace terminates the wrong scope, indentation shows it instantly. In a flat wall of text the same mistake is invisible until something downstream fails with a missing-key error.

Error location. This is the payoff this guide is about. An error reported at “line 12, column 9” is only usable if your input actually has lines and columns. If your JSON is one long line, every error is reported at line 1 with a column number in the thousands. Format first, then debug — the error location becomes meaningful.

There is a legitimate counter-case: transport size. A real example from this guide’s worked data — an order object with a customer and two line items — is 286 bytes pretty-printed at two-space indent and 179 bytes minified, a 37 percent reduction. That is why the common convention is pretty in the repository and logs, minified on the wire. The two forms convert between each other losslessly, because both are produced by serializing the same parsed value.

What a formatter actually does#

Understanding this saves you a class of confusion later. A real formatter does not edit your text. It does three steps:

  1. Parse the input with a real JSON parser. If parsing throws, nothing else happens — you get the validation error instead.
  2. Optionally transform the parsed value — sort object keys alphabetically, for example.
  3. Serialize the value back to text with your chosen indentation.

Two consequences follow. First, formatting is normalizing: it produces the canonical serialization of your data, not your original text with whitespace inserted. Comments you thought were fine, duplicate keys you never noticed, 1.50 versus 1.5 — all of it resolves during the parse step, and the output shows you what the parser actually saw. Second, formatting is validation with extra output: a document that formats cleanly is a document that parses cleanly, full stop. There is no “valid but won’t format” state.

Indentation choice is a style decision with one practical edge: two spaces and four spaces are spaces (survive any terminal and copy-paste), while tab indentation produces the most compact pretty output and pastes correctly into editors configured for tabs. Pick one per project and stop discussing it. Key sorting is worth enabling when you are comparing two documents that may have the same content in different key order — sorted serialization makes the comparison textual again.

The five errors you will actually meet#

JSON’s grammar is small and unforgiving. In practice, almost every invalid JSON you will see is one of these five. Each example below is real: the messages are what a modern engine produces, and the locations are computed exactly as the formatter on this site computes them.

1. Single quotes instead of double quotes. JSON strings and keys must use double quotes; single quotes are a JavaScript habit the grammar rejects.

{
  'host': 'api.example.com',
  "port": 443
}

The engine reports: Expected property name or '}' — located at line 2, column 3, which is precisely the first single quote. The column points at the character the parser choked on, so the fix is usually to look at that exact spot.

2. Trailing commas. Convenient in JavaScript arrays and objects; illegal in JSON.

{
  "users": [
    {"id": 1, "name": "Ada"},
    {"id": 2, "name": "Linus"},
  ],
  "total": 2
}

The message is Unexpected token ']' — the parser was expecting another value after the comma on the previous line and found the closing bracket instead. The reported position is the ], but the defect is the comma before it, on line 4. Learning to look one token back is half the skill of reading these errors.

3. A missing comma between members. The mirror image:

{
  "retries": 3,
  "timeout": 30
  "region": "eu-west"
}

Here the parser reads "timeout": 30, then expects , or } and finds a string instead: Expected ',' or '}' after property value. The position points at the start of "region" — the line where the next member begins, which is one line below the comma you forgot.

4. Unescaped characters inside strings. A literal newline, or a quote, inside a string value:

{
  "note": "line1
line2"
}

The engine rejects the raw line break with Bad control character in string literal, pointing at the position just after line1. Strings must escape control characters as \n, and internal double quotes as \". This error also appears when someone copies a JSON snippet out of a log or chat window and the copy process broke a line.

5. The wrong kind of number or literal. JSON allows no comments, no NaN, no Infinity, no hexadecimal, and no leading zeros. {"ts": 0x1F}, {"n": NaN}, and // comment are all hard parse errors. The messages vary (Unexpected token 'x', Unexpected token 'N'), but the category is the same: the grammar only admits what it admits.

One meta-observation: all five are lexical errors — the parser never got to questions of structure or content. JSON has no other kind of error at parse time, which is why the error list is so short and so learnable.

How row-and-column location is computed#

When a parser throws, it attaches a character offset — the number of characters from the start of the input to the point of failure. An offset alone is hostile to humans (“position 4”), so the formatter converts it: walk the input from the beginning, incrementing the column for each character and resetting it at every line break. The result is a one-based row and column you can find with your editor’s line-number gutter.

Knowing this conversion explains two quirks you may have noticed. First, if the input is a single line, every offset maps to row 1 — format your input (or at least give it line breaks) and locations become usable. Second, if the engine cannot extract an offset from its message at all, a robust validator falls back to a scan or reports the end of input rather than a fake location. A validator that always claims “line 1, column 1” regardless of the actual failure is not locating your error; it is guessing.

Practical workflow for a stubborn document: paste it into the formatter, read the row and column, jump there in your editor, fix, repeat. Large documents often have more than one error, because the author made the same mistake (trailing commas, say) in several places — each fix reveals the next location, and three or four iterations is normal.

Worked example: format, then query#

Once a document parses, formatting is only the beginning — usually you want to extract something. This is where a query language earns its place. Take this order object, formatted at two-space indent (286 bytes; 179 minified):

{
  "order": "A-3782",
  "customer": {
    "id": 9042,
    "email": "[email protected]"
  },
  "items": [
    { "sku": "KB-01", "qty": 1, "price": 129.00 },
    { "sku": "CB-02", "qty": 2, "price": 18.50 }
  ],
  "total": 166.00,
  "currency": "USD"
}

JSONPath expressions against this document, with their verified results:

  • $.total returns 166, at JSON Pointer /total
  • $.customer.email returns [email protected]
  • $.items[*].sku returns both SKUs — KB-01 and CB-02 — at /items/0/sku and /items/1/sku
  • $..price finds prices at any depth: 129 and 18.5
  • $.items[?(@.qty > 1)].sku filters on a condition and returns CB-02 — the item with quantity greater than one

Note the two last examples. The recursive descent $.. is how you answer “where does this key appear anywhere in the document?” — invaluable in configs you did not write. The filter expression [?(@.qty > 1)] answers “which entries satisfy a condition?”, which is a query you would otherwise write a five-line script for. A query tool that highlights matched spans in the formatted output effectively turns JSON debugging into a visual task: the path tells you what matched, the highlight shows you where.

Try it on this site#

Two tools cover this entire workflow, and both run entirely in your browser — no upload, no backend, safe with real payloads.

Start with the JSON formatter and validator. Paste any of the five broken examples above (retype the single-quote or trailing-comma ones) and watch the error come back with its row and column; then paste the order document and switch between two-space, four-space, and tab indentation, toggle key sorting to see the canonical sorted form (top-level keys become currency, customer, items, order, total), and compare the byte counts of pretty versus minified output.

Then take the same order document to the JSONPath query tool. Run each of the five expressions above and confirm the results; the matched values are highlighted at their positions in the formatted document. When a real API response lands on your desk, this combination — format to validate, query to extract — replaces most of the throwaway scripts used to inspect it.

FAQ#

Tabs or spaces for JSON indentation?#

Either is valid; consistency is what matters. Two spaces is the most common convention in modern tooling because it stays narrow in deeply nested documents. Tabs produce smaller pretty output. Since any formatter converts between representations losslessly, an automated check (format the file, fail the build if it changed) settles the debate permanently.

Why are trailing commas an error? They seem harmless.#

Because the grammar says a comma separates members, so a comma before } promises a member that never arrives. Some parsers offer a lenient mode that strips trailing commas before parsing — useful for reading third-party files you cannot fix — but enabling leniency by default in your own pipeline hides genuine mistakes, such as a member that was deleted and left its comma behind.

Does key order in JSON matter?#

Not to a conforming parser — objects are unordered by definition. It matters to humans and diffs: stable, sorted keys make two serializations of equal content textually equal, which makes review and comparison trivial. That is exactly what the key-sorting option does: it canonicalizes without changing meaning.

My JSON is valid but huge. Any guidance?#

Format for inspection, minify for transport, and query instead of scrolling. A document too large to read is exactly when a query expression ($..price) plus span highlighting outperforms eyeballing. Also be aware that very large single-line inputs make error columns astronomically large — give the text line breaks before debugging it.

Can JSON contain comments?#

No — the grammar has no comment production, which is why config formats that need comments either extend JSON (adding comment support in their own parsers) or use a different format entirely. Stripping comment lines with a regex before parsing is a common workaround, and a lenient formatter does it more safely, but the result is no longer JSON.

Summary#

Formatting is validation with a human-readable output; the parse step is the proof of correctness, and the serialization is for your eyes, your diffs, and your reviewers. The error messages look cryptic until you learn the five real categories — quoting, trailing commas, missing commas, unescaped characters, and non-grammar literals — and learn that the reported position may point one token past the defect. Row-and-column location converts an engine offset into something your editor can jump to; querying converts a validated document into answers.

Practice the full loop on this site: validate and format with the JSON formatter, then extract and highlight with the JSONPath query tool.

← All guides