All posts

Why Your JSON Validator Keeps Failing (and It's Probably Not the JSON)

5 min read

Learn the four hidden reasons your JSON validator rejects valid-looking data: invisible characters sneaking in from terminals, trailing commas that JavaScript allows but JSON doesn't, and values like NaN that serialize into nothing.

Someone copies a JSON blob into a JSON validator, gets an error, and assumes the JSON is broken. Most of the time, the JSON is fine. The problem is invisible characters copied from a terminal, trailing commas that JavaScript allows but JSON doesn't, or serialization edge cases that strip data silently.

Invisible Characters in JSON

When you copy JSON from a terminal or email, you pick up invisible characters most JSON validators won't show you:

Character Looks like What it breaks
Zero-width space (U+200B) Nothing Key names with invisible gaps
Non-breaking space (U+00A0) Regular space Parser sees unexpected token
Tab (U+0009) Spaces if you're lucky Indentation errors
UTF-8 BOM (U+FEFF) Nothing First key gets a hidden prefix
En dash (U+2013) Hyphen Number parsing fails

A team spent hours debugging a 400 error from an API. The JSON payload validated correctly in three different tools. The problem was invisible newline characters embedded in a key name, picked up from copying the JSON from a terminal with wrapped lines. The validator said "unexpected token at position 347" and left them guessing. It never showed them the actual bytes at that position.

The UTF-8 BOM (U+FEFF) is a common offender. Some editors and terminals add it silently. Your first key ends up with an invisible prefix that parsers can't handle. Validators rarely flag it with a useful message.

The JSON Validator on this site detects invisible characters and shows the exact byte at the error position.

Trailing Commas

JSON does not allow trailing commas. JavaScript objects do. This mismatch causes a specific, predictable failure: you write JSON in a .js or .ts file where trailing commas work fine, then move it to a .json file or send it over HTTP, and suddenly it breaks.

{
  "name": "John",
  "age": 30,
}

That comma after 30 is invalid. Your brain tells you "but this worked five seconds ago" and you waste time figuring out why. The same applies to arrays. A trailing comma after the last element fails identically.

The JSON spec (RFC 8259) is unambiguous: no trailing commas, ever. But some API gateways and HTTP proxies strip trailing commas before forwarding the payload. This masks the problem during local development and only surfaces it when requests reach a backend with stricter parsing. If you've ever had a request that works in Postman but fails in production, trailing commas are a plausible culprit.

NaN, Infinity, and undefined Get Silently Stripped

JSON has no concept of NaN, Infinity, or undefined. How JavaScript handles them during serialization depends on the type:

const data = {
  score: NaN,
  max: Infinity,
  name: "test",
  removed: undefined
};

JSON.stringify(data);
// Result: {"score":null,"max":null,"name":"test"}

NaN and Infinity become null. The key isn't dropped; the value is replaced. Only undefined values cause keys to disappear from the output entirely. An API endpoint that calculates a division producing NaN doesn't lose the field; it sends null instead. The client receives something, but it's not what anyone intended.

This is the kind of bug that lives in production for weeks because test data never triggers the edge case. The API looks fine in staging, and under real traffic a specific calculation silently starts producing null values.

The real challenge is that different languages handle these edge values inconsistently:

Language NaN Infinity undefined / None
JavaScript (JSON.stringify) Becomes null Becomes null Key dropped in objects
Python (json.dumps, default) Raises ValueError Raises ValueError Raises TypeError
Python (json.dumps, allow_nan=True) Writes NaN (invalid JSON) Writes Infinity (invalid JSON) Raises TypeError
Go (encoding/json) Returns error at serialize time Returns error at serialize time Returns error at serialize time
Java (Jackson) Throws exception by default Throws exception by default Skipped by default

A value that serializes fine in one service breaks when consumed by another. The error message rarely points to the root cause; it shows up as a generic parsing failure on the receiving end.

Single Quotes

JSON requires double quotes for strings. Not single quotes, not backticks.

// Not valid:
{ 'name': 'value' }

// Valid:
{ "name": "value" }

JavaScript runtimes parse single-quoted "JSON" fine because it's actually a JavaScript object literal. But when you send it to an API or write it to a .json file, it fails. Most JSON validators will tell you it's wrong but not why. The error position points to the first single quote, but the real issue is that you're writing JavaScript syntax in a JSON context.

Next time a validator says "unexpected token" and you can't see why, paste it into the JSON Validator and check for invisible characters first. That's the case that wastes the most time by far.