Command Palette

Search for a command to run...

Why Is My JSON Invalid? Common Errors and Fixes

Why Is My JSON Invalid? Common Errors and Fixes

T
Toolz Team
|Sep 13, 2026|16 min lezen

Onderdeel van de collectie Datatools

JSON Validator

Valideer JSON tegen RFC 8259 en krijg de exacte regel, kolom en teken van de eerste syntaxisfout, plus detectie van dubbele sleutels en structuurstatistieken. Clientzijde, geen aanmelding.

JSON Validator gebruiken

I build toolz.dev, and before that I spent years shipping WordPress plugins and Laravel APIs. In all of it, the single error message I have wasted the most hours on is some variation of "Unexpected token in JSON at position 428." It tells you almost nothing. There is no line, no column, and the position it names is frequently a few characters past the thing that is wrong. You end up counting characters in a minified blob by hand, or pasting the whole payload into an editor and squinting. I built the JSON Validator so I never have to do that again, and this is the guide to what it does and why validating JSON is trickier than it looks.

TL;DR: A JSON validator checks text against the grammar in RFC 8259 and, when the text is broken, tells you the exact line, column, and character of the first error, phrased in terms of what it expected. It also catches things a normal parser hides: duplicate keys, which get silently collapsed to the last value, and it reports the structure of a valid document (object, array, and key counts, nesting depth, and byte size). It is not a formatter. A formatter assumes the input is already valid; a validator is the thing you reach for precisely when it is not.

What does it mean for JSON to be valid?

JSON is a small format with a strict grammar. The current standard is RFC 8259, which lines up with ECMA-404, and between them they define exactly what a valid document may contain. A JSON text is a single value. That value is an object, an array, a string, a number, true, false, or null. Objects are wrapped in braces and hold comma-separated "key": value pairs. Arrays are wrapped in brackets and hold comma-separated values. Strings are wrapped in double quotes, never single quotes. Numbers follow a specific shape with no leading zeros and no trailing decimal point. There are no comments, and there are no trailing commas.

That last sentence is where most real-world JSON goes wrong, because a lot of the JSON people write by hand is not really JSON. It is a config file with // comments, or an object copied out of JavaScript source where single quotes and trailing commas are fine, or a hand-edited fixture where someone added a field and left a dangling comma. All of those are rejected by a strict parser, and a validator's job is to reject them at a specific spot rather than with a shrug.

One point worth clearing up, because it trips up people who learned JSON years ago: a bare value is a complete, valid JSON document. 42 on its own is valid JSON. So is "hello", and so is true. The old RFC 4627 required the top level to be an object or an array, but that restriction was dropped in 2014 when RFC 7159 (later 8259) superseded it. If a tool tells you 42 is not valid JSON, that tool is following a spec that has been obsolete for over a decade.

Why is JSON.parse such a bad error reporter?

The reason a dedicated validator earns its place is that the parser built into your language is optimised for the happy path, not for explaining the sad one. JSON.parse in a browser throws a SyntaxError whose message is engine-specific and usually gives you a character offset with no line or column. V8 says "Unexpected token } in JSON at position 148." Firefox says "JSON.parse: unexpected character at line 1 column 149." Safari says something different again. None of them tell you what they expected, and the position is measured from the start of the string, which is useless the moment your JSON has more than a couple of lines.

The validator on toolz.dev does not use JSON.parse. It walks the text itself with a hand-written recursive-descent parser, tracking the line and column as it goes, so when it stops it can hand you all three coordinates: the line, the column, and the absolute character offset. More importantly, it knows the grammar rule it was in the middle of, so the message is specific. Instead of "unexpected token," you get "Trailing comma is not allowed before the closing brace," or "Object keys must be double-quoted strings," or "Expected a comma or a closing brace after an object member." That difference is the difference between fixing the file in five seconds and hunting for it.

It also draws a caret under the exact character on the offending line, the way a compiler does. When you are looking at line 34 of a config file, "column 12" is fine, but a ^ sitting directly under the stray quote is faster.

Which JSON mistakes does the validator catch by name?

These are the failures I see most often, both in my own work and in the support threads for tools like this. The validator names each one rather than reporting a generic token error.

Mistake What it looks like Why it is invalid
Trailing comma {"a": 1,} or [1, 2,] JSON has no trailing commas; the standard's grammar simply does not allow one before a closing brace or bracket
Single quotes {'name': 'Liton'} Strings and keys must use double quotes; single quotes are a JavaScript habit, not JSON
Missing comma {"a": 1 "b": 2} Every member after the first must be preceded by a comma
Comments {"a": 1} // note JSON has no comment syntax; the // and everything after it is unexpected content
Unquoted key {name: "Liton"} Keys are strings and must be quoted, unlike JavaScript object literals
Leading zero {"n": 007} A number may not have a leading zero, so 007 is invalid; write 7
Trailing decimal point {"n": 5.} The fraction after a decimal point needs at least one digit
Raw control character a literal tab or newline inside a string Control characters inside strings must be escaped as \t, \n, and so on
NaN or Infinity {"n": NaN} These are valid in JavaScript but are not JSON values
Unterminated string {"a": "oops} A string with no closing quote runs to the end of the input

Every one of these produces a plain-language message and a position. The leading-zero case is a nice example of why a purpose-built parser helps: a generic parser reads 007 as the number 0 and then trips over the 7 as "extra content," pointing you at the 7, when the real problem is the two zeros in front of it. The validator checks for the leading-zero pattern first and tells you that directly.

What about duplicate keys?

Here is the one that has bitten me in production more than any syntax error, because it is not a syntax error at all. This is valid JSON:

{
  "timeout": 30,
  "retries": 3,
  "timeout": 60
}

RFC 8259 says the names within an object "SHOULD be unique," but it does not forbid duplicates, so a parser is required to accept this. What happens next is where the trouble starts: the standard says the behaviour with duplicate names is unpredictable, and in practice almost every parser keeps the last value and silently discards the earlier ones. So this object parses to {"timeout": 60, "retries": 3}, and the "timeout": 30 you carefully set at the top of the file is gone without a warning. If two people edited the config, or a merge went wrong, or a templating step emitted the same key twice, you have a bug that no parser will ever flag for you.

The validator flags it. Duplicate-key detection is on by default, and because a duplicate is legal, it is reported as a warning rather than an error: the document is still valid, but you are told every key that appears more than once in the same object, with its line and a JSON Pointer path so you can find it inside a nested structure. If you genuinely want duplicates, you can turn the check off. Most of the time, seeing the warning is the moment you realise what went wrong.

How is a validator different from a formatter?

People use the words interchangeably, and the tools overlap, but they answer different questions. A JSON formatter takes JSON you already believe is valid and re-prints it with consistent indentation so a human can read it. If you hand a formatter broken JSON, the best it can do is refuse. A validator's whole purpose is the broken case: it tells you where and why. The toolz.dev validator does both jobs, because once it has confirmed a document is valid it has already parsed it, so it pretty-prints the result in the same pass. But the headline feature is the diagnosis, not the indentation.

The same relationship holds with the neighbouring tools. The JSON minifier strips whitespace to shrink a payload, which only makes sense on valid input. The JSON sorter reorders keys for stable diffs, again on valid input. The JSON escape and unescape tool deals with embedding JSON inside a string. And the JSONPath tester queries a document you have already confirmed is well-formed. Validation is the step that comes before all of them, which is why it is the one I run first whenever an API response or a config file is behaving strangely. There is a broader tour of how these fit together in the ultimate guide to JSON tools.

What do the structure statistics tell you?

When a document validates, the tool reports its shape: how many objects and arrays it contains, how many keys, how many strings, numbers, booleans, and nulls, how deeply it nests, and its size in both characters and UTF-8 bytes. This is not decoration. When you are handed an unfamiliar API response, the depth number tells you at a glance whether you are dealing with a flat record or a deeply nested tree, and the key count tells you roughly how much work parsing it will be.

The byte figure matters more than people expect. Request-size limits, message-queue limits, and database column limits are almost always measured in bytes, not characters, and the two differ the moment your JSON contains anything outside ASCII. An emoji or an accented name is one character on screen but two, three, or four bytes on the wire. If a payload is bumping against a 256 KB limit, the character count will mislead you and the byte count will not. The validator computes the true UTF-8 byte length so the number you see is the number the limit is counting.

How I use it

My workflow is boring and fast, which is the point. When an integration breaks, I copy the raw response body straight out of the network tab and paste it into the validator. If it is valid, the green result and the stats tell me the problem is somewhere else, in my code rather than their data, and I have ruled out a whole class of causes in two seconds. If it is invalid, I get the line and column, fix it, and move on. When I am hand-editing a config file, I paste it in before committing, mostly to catch the trailing comma I always seem to leave behind and the occasional duplicate key from a copy-paste. For anyone assembling their own kit, the validator sits alongside the other essentials in the web developer toolkit.

The privacy angle is not an afterthought either. The JSON people most need to validate is exactly the JSON they should be most careful with: API responses carrying tokens, config files with credentials, payloads with customer data. Everything the validator does happens in your browser, in JavaScript, on your machine. Nothing is uploaded, logged, or stored. You can prove it by opening the network tab while you validate, or by disconnecting from the internet and watching the tool keep working.

Frequently asked questions

Why does my JSON say it is invalid when it looks fine?

The commonest reasons are a trailing comma before a closing brace or bracket, single quotes where JSON requires double quotes, a missing comma between two members, or a comment, which JSON does not allow at all. All four are easy to miss by eye, especially in a large file. The validator names which one it found and gives you the exact line and column, so instead of scanning the whole document you jump straight to the character it stopped on.

Is a single number or string valid JSON on its own?

Yes. Under RFC 8259, any JSON value is a complete JSON text, so 42, "hello", true, and null all validate by themselves. The rule that the top level had to be an object or an array came from the older RFC 4627 and was removed in 2014. A tool that rejects a bare value is following an obsolete specification.

Does the validator change or upload my data?

No. It parses the text in your browser and never sends it anywhere. On a valid document it produces a pretty-printed copy for convenience, but that copy is generated locally and the original is never transmitted, logged, or stored. Because there is no server round trip, the tool also works with the network disconnected once the page has loaded.

Why should I care about duplicate keys if the JSON is still valid?

Because a duplicate almost always means data loss you did not intend. The standard permits duplicate keys but leaves their handling undefined, and in practice parsers keep only the last value and drop the rest. So a config where timeout appears twice quietly loses the first setting. The validator warns you about every repeated key with its position, which is usually the moment you spot a bad merge or a copy-paste mistake.

Can this validate JSON with comments, like a tsconfig or VS Code settings file?

No, and that is deliberate. Those files are JSON with Comments (JSONC) or JSON5, which are separate formats that relax the rules. The validator targets strict RFC 8259, so it will flag a comment as an error. If you need comments in a config file, that is a signal the format should be something built for it, such as YAML or TOML, rather than JSON stretched past what the standard allows.

What is the difference between validating and formatting JSON?

Validating checks whether the text obeys the JSON grammar and, if it does not, tells you exactly where it breaks. Formatting assumes the text is already valid and only adjusts its whitespace for readability. You reach for a validator when something is wrong and you need to know what; you reach for a formatter when everything is fine and you just want it readable. This tool does both, but its reason to exist is the diagnosis.

How big a file can I validate?

Comfortably into the tens of megabytes, limited only by your browser's memory, because the whole document is parsed in one pass on your device. There is no upload size cap because there is no upload. For a genuinely huge file, in the gigabytes, a streaming command-line validator is the better instrument, since the browser holds the entire text in memory at once.

Comments

0 comments

0/2000 characters

No comments yet. Be the first to share your thoughts!