JSON-LD for Beginners: Why It Must Be Valid
Learn what JSON-LD is, why even one missing comma can stop Google from reading your schema, and how to validate it before publishing.
What you will learn in this guide
- What JSON-LD is and where it sits in your HTML
- Common JSON syntax errors and why they break schema
- How to validate JSON-LD before publishing
- How to fix the 5 most common errors
- How Google handles invalid JSON-LD (silently ignored)
1 What is JSON-LD?
JSON-LD is a way of describing your page content to search engines in a structured format. It sits in a <script type="application/ld+json"> tag in the head or body of your HTML.
2 The 5 most common JSON errors
| Error | Example | Fix |
|---|---|---|
| Trailing comma | {"a":1,"b":2,} | Remove the comma before } |
| Missing comma between properties | {"a":1 "b":2} | Add comma between key-value pairs |
| Unescaped quotes inside strings | "name":"O'Reilly" with raw apostrophe is fine; "name":"He said "hi"" is not | Escape inner quotes: \" |
| Single quotes instead of double | {'a':1} | JSON requires double quotes around keys and string values |
| Unquoted keys | {a:1} | Always quote keys: {"a":1} |
3 How to validate your JSON-LD
- 1Copy your JSON-LD blockFrom your page source, copy everything between
<script type="application/ld+json">and</script>. - 2Paste into the JSON ValidatorOpen schema-tools.html#json-checker and paste your snippet. The tool reports the line and column of any error.
- 3Run Google's Rich Results TestEven if JSON is valid, Google may reject the schema for missing required fields. Use Google's tool to confirm rich-result eligibility.
- 4Fix and re-testRe-validate after every change. Don't publish until both validators pass cleanly.
4 When automation breaks JSON
Most schema problems come from generators that build JSON from a template. Watch for:
- Smart quotes from a CMS WYSIWYG (curly quotes break JSON)
- User-supplied content with unescaped quotes (product reviews, author names)
- Currency symbols entered as HTML entities instead of plain chars
- Missing fallback values when a field is empty
4 Why this bug is worse than a bug that crashes
Almost every defect in software announces itself. A page errors, a build fails, a test goes red, a customer complains. Broken JSON-LD does none of that.
Google reaches a block it cannot parse, discards it, and carries on. The page renders perfectly. Nothing appears in Search Console. No email arrives. The only symptom is an enhancement that stops appearing — and it stops appearing gradually, as pages are recrawled over days or weeks, so there is not even a clean date to correlate against a deploy.
The result is that structured data on most sites is not maintained; it is abandoned, working or not, until somebody happens to look. And the thing that broke it was almost never a person editing schema. It was a plugin update, a theme change, or a bulk edit that nobody connected to markup at all.
5 Where these errors really come from
The five syntax errors listed above are what you see. It is worth understanding what produces them, because the fix is almost never "be more careful".
The unescaped quote is a template bug. Somebody enters a product name containing a double quote, or a description with a quoted phrase, and the template drops that value straight into a JSON string. The quote terminates the string early, and the parse dies. The answer is not to forbid quotation marks in your content — it is to stop building JSON by gluing strings together. Any system that concatenates its way to JSON-LD will produce this bug eventually; a system that serialises a real object structure cannot.
The trailing comma is a hand-editing bug. Somebody removed a property and left the comma behind. It is the signature of markup being maintained by hand, which is itself the deeper problem: schema that is written once and edited by hand will drift out of step with the page it describes within months.
Single quotes and unquoted keys are a copy-paste bug. Somebody pasted a JavaScript object literal, which is valid JavaScript and is not JSON. The two look almost identical and are not the same language.
The pattern in all three: the error is a symptom of how the markup is produced. Generate it from your data, serialise it properly, and this whole class of failure stops happening.
6 Two validators, and the thing neither of them checks
Fixing the syntax gets you past the first gate. There are three, and most sites stop after one.
- 1Does it parse?A JSON validator answers this. It is necessary and it proves very little — a block can parse cleanly and describe nothing Google cares about.
- 2Does it qualify?Google's Rich Results Test answers this. Valid JSON with a missing required property, or a type that is not eligible for any enhancement, will be parsed perfectly and used for nothing.
- 3Is it true?No validator answers this, and it is the one that costs money. Markup asserting a price the page does not show is valid JSON, valid schema, and a false statement about your own product. In Google's shopping surfaces that is grounds for item disapproval; organically the enhancement can simply be dropped. And it misleads the shopper who clicked expecting the price they were shown.
So the check worth automating is not "does it validate" but "does the value in the JSON-LD equal the value on the rendered page". Price against price. Availability against the state of the Add to Cart button. Headline against the H1. That comparison is the only thing standing between you and markup that is confidently, validly wrong.
7 Make the check automatic, or it will not happen
Structured data breaks at deploy time, because that is when the code that generates it changes. Which means a manual validation step — however well-intentioned — will be skipped on precisely the release that needed it, and the breakage will then sit undetected for as long as it takes somebody to notice a missing enhancement.
The efficient shape of this is template-level. One product template renders every product; a regression in it breaks all of them at once, so a single watched URL detects the problem for the cost of one fetch. Conversely, watching a hundred product pages while watching no article page leaves an entire template unobserved.
So: for every template that emits structured data — product, category, article, author, the homepage — take one instance, fetch it on every deploy and on a schedule, and assert that it parses, that the required properties are present, and that the values agree with the page.
Run it on a schedule as well as on deploys, because plugin auto-updates and CDN configuration changes are deploys that nobody on your team made.
8 Frequently asked questions
Nothing visible, which is exactly the problem. Google discards a block it cannot parse and moves on. There is no warning, no email, and no Search Console error — the enhancement simply stops appearing, and it stops appearing gradually as pages are recrawled, so there is not even a clear date to correlate against. This is why structured data breaks silently and stays broken for months: every other kind of bug announces itself, and this one does not.
Because JSON is parsed as a whole, not line by line. A parser reading your Product block hits the malformed character, cannot construct a valid object, and abandons the entire script tag — not just the property that was wrong. So a stray comma in the last property of a fifty-line block discards all fifty lines, including the forty-nine that were perfect. There is no partial credit, and that is why validation before publishing is not optional.
A CMS field containing a double quote, almost always. Somebody types a product name with quotation marks in it, or a description containing a quoted phrase, and the template drops that value into a JSON string without escaping it. The quote terminates the string early and the parse fails. The fix is not to ban quotes from your content — it is to escape values properly when generating the markup, which is the template's job. Any system building JSON-LD by string concatenation will eventually produce this bug; systems that serialise a real object structure will not.
No, and this catches people out after they have fixed the syntax. Valid JSON is the first gate; valid schema is the second. Google can parse your block perfectly and still reject the enhancement because a required property is missing, or because the type does not qualify for any rich result. So run two checks: a JSON validator to confirm it parses, then Google's Rich Results Test to confirm the schema qualifies. Passing the first tells you nothing about the second.
Whether the markup is true. A Product block asserting £29.99 while the page displays £34.99 is perfectly valid JSON and perfectly valid schema — it parses, it has every required field, and it passes every automated test in existence. It is also a false statement about your own product in machine-readable form, which in Google's shopping surfaces is grounds for item disapproval and organically can get the enhancement dropped. Structured data must describe the visible page, and only a comparison between the two catches a violation.
Assert it automatically, on every deploy. Structured data breaks at deploy time — a plugin update, a template change, a bulk edit — and it breaks invisibly, which means a manual check will be skipped on exactly the release that needed it. Take one representative page per template, fetch it, and assert three things: the JSON-LD parses, the required properties are present, and the values match the rendered page. One watched URL per template detects a regression across every page that template renders.
Either works — Google reads it from both. What matters far more is that it is in the HTML your server sends, rather than injected by JavaScript after load. Google does render, so script-injected schema can be picked up, but rendering is queued and less certain than markup that was simply there. And many AI retrieval fetchers parse the served HTML and never run JavaScript at all, so schema injected client-side is invisible to them permanently. Put it in the served response and the question disappears.