The JSON-LD errors that make structured data unreadable or untrustworthy, from trailing commas to conflicting plugin blocks, with broken and fixed snippets.
By Crawlito · 9 min read
In short
JSON-LD must be strict JSON, so one trailing comma, a curly quote or an unescaped line break makes the whole block fail to parse.
JSON-LD keys, keywords and values are case-sensitive, so a block typed "product" does not describe a schema.org Product.
Google says items missing required properties are not eligible for rich results, and a Product snippet needs a name plus offers, review or aggregateRating.
Google's guidelines say structured data must be a true representation of the page, so a price or stock status that differs from the visible page breaks those guidelines.
Google can read JSON-LD injected by JavaScript, but crawlers that read only the raw HTML never see it, so check the page source as well as the Rich Results Test.
Most JSON-LD errors fall into two groups. The first group stops the block from parsing at all: trailing commas, curly quotes, unescaped characters and stray HTML. The second group parses fine but says the wrong thing: a missing or misspelled @context, types that do not exist, missing required properties, duplicate blocks from different plugins, and values that contradict the page. Below is each error with a broken snippet, the fixed version and a way to check it.
Use three views of the same page, because each one catches something the others miss.
The raw HTML. This is what a crawler that does not run JavaScript receives. Extract every block and parse it:
bash
curl -s https://example.com/products/oak-table | python3 -c '
import sys, re, json
html = sys.stdin.read()
blocks = re.findall(r"<script[^>]*application/ld\+json[^>]*>(.*?)</script>", html, re.S | re.I)
print(len(blocks), "JSON-LD block(s)")
for i, block in enumerate(blocks, 1):
try:
data = json.loads(block)
items = data if isinstance(data, list) else data.get("@graph", [data])
print(i, "ok:", ", ".join(str(item.get("@type")) for item in items))
except json.JSONDecodeError as e:
print(i, "parse error:", e)
'
Typical output on a page with problems:
4 JSON-LD block(s)
1 parse error: Illegal trailing comma before end of object: line 2 column 75 (char 75)
2 parse error: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
3 ok: Organization
4 parse error: Invalid control character at: line 1 column 26 (char 25)
The exact wording varies with your Python version, but the line and column point at the problem. How to see your page as an AI crawler, with curl covers sending a crawler's user agent with the same request.
The Schema Markup Validator. It checks syntax and schema.org vocabulary for any type, not only the ones Google uses.
Google's Rich Results Test. It reports missing required and recommended properties for the rich result types Google supports. After you deploy, the rich result reports in Search Console show the same problems across the whole site.
A pass in the Rich Results Test does not prove the markup is in your raw HTML. Google says it "can read JSON-LD data when it is dynamically injected into the page's contents" (Google), so a block added by JavaScript can pass there and still be missing from the curl output above. Crawlers that read only the server's HTML will not see it; do AI crawlers run JavaScript? has the evidence.
Parse errors: when the block is not valid JSON
JSON-LD is JSON, and JSON is strict. RFC 8259 allows strings only in straight double quotes (U+0022), requires every comma to be followed by another value, has no comment syntax, and requires quotation marks, backslashes and control characters such as line breaks to be escaped inside strings. Break any of those rules and a parser rejects the whole block, not just the bad line.
Trailing commas, comments and single quotes
These usually come from hand-editing or from copying a JavaScript object into a template.
Text pasted from a document editor, or a CMS field with "smart quotes" turned on, turns " into “ and ”. They look almost the same and are not valid JSON delimiters.
Curly quotes inside a value are fine ("name": "The “Oak” table"). Only the quotes that wrap keys and strings must be straight.
Unescaped quotes, backslashes and line breaks
This happens when a template prints a field without encoding it. A product called 18" oak shelf, a Windows path or a description with a line break each produce invalid JSON.
{
"name": "18\" oak shelf",
"description": "Solid oak.\nShips flat."
}
The lasting fix is to stop building JSON by string concatenation. Build an object and serialize it: json_encode() in PHP (wp_json_encode() in WordPress), JSON.stringify() in JavaScript, json.dumps() in Python. They escape every quote, backslash and control character that JSON requires. Not all of them protect against the </script> problem in the next section, which needs one more step.
HTML and </script> inside strings
A <script type="application/ld+json"> element is raw text to the HTML parser. Two things follow.
First, the sequence </script> anywhere inside a string ends the element early. The rest of your JSON spills into the page and the block no longer parses. The JSON-LD 1.1 specification tells authors to avoid character sequences that could be confused with comment or script delimiters. The safe fix is a JSON escape: write < as \u003c, or / as \/. Both are valid JSON escapes, so a JSON parser turns them back into the original characters, but the HTML parser never sees </script>.
Broken:
html
<script type="application/ld+json">
{ "description": "Embed code: </script> is not allowed here" }
</script>
Fixed:
json
{ "description": "Embed code: \u003c/script> is not allowed here" }
Second, HTML entities are not decoded inside a script, so & stays as the literal text &, and tags copied from a rich-text field stay as tags:
Broken:
json
{ "description": "<p>Solid oak & walnut, <strong>made to order</strong>.</p>" }
Fixed:
json
{ "description": "Solid oak & walnut, made to order." }
Strip tags and decode entities before serializing. In WordPress, wp_strip_all_tags() followed by html_entity_decode() and then wp_json_encode() covers it. PHP's encoder escapes / as \/ by default, which also neutralizes </script>, unless the code passes the JSON_UNESCAPED_SLASHES flag. JSON.stringify() and Python's json.dumps() leave < and / as they are, so after serializing, replace every < in the output with \u003c.
Context and type errors: valid JSON, wrong vocabulary
A block can parse perfectly and still describe nothing a consumer recognizes.
A missing, misspelled or partial @context
@context tells a JSON-LD processor what your keys mean. Without it, "name" is just a string key, not schema.org's name.
@context value
Result
"https://schema.org"
Correct
"http://schema.org"
Correct: schema.org's FAQ says both forms are fine
"schema.org"
No protocol, so a JSON-LD processor resolves it as a relative URL, not the schema.org context
"https://shema.org"
A typo that points at an unrelated host
"@Context" or "context"
Not the keyword at all
Missing
Terms are not tied to any vocabulary
The JSON-LD 1.1 specification is explicit: "All keys, keywords, and values in JSON-LD are case-sensitive." That rule matters for the next error too.
Types and properties that do not exist
schema.org types are capitalized and spelled exactly. Lowercase, plural and invented types all fail quietly.
Other common ones: BlogPost (the type is BlogPosting), LocalBuisness (LocalBusiness), and Products. The Schema Markup Validator flags unknown types and properties; look each one up at schema.org when in doubt.
Values in the wrong format
Dates and prices cause most of these. Google's Article documentation asks for ISO 8601 dates and recommends a time zone, and says to put only the author's name in author.name. For prices, schema.org's price guidelines say to use a dot as the decimal separator and to give the currency in priceCurrency as an ISO 4217 code such as USD, not as a symbol like $ inside the value. Google's product snippet documentation lists priceCurrency as recommended for product snippets and required for merchant listings.
Broken:
json
{
"datePublished": "08/11/2026",
"author": { "@type": "Person", "name": "Posted by Jane Doe, Head of Content" },
"offers": { "@type": "Offer", "price": "$1,299.00" }
}
Google states that "Items that are missing required properties are not eligible for rich results" (Google). Each feature has its own list, so check the page for your type rather than guessing:
Type
Required by Google
Source
Product (product snippets)
name, plus one of offers, review or aggregateRating
A typical Product failure: a theme outputs name, image and description but drops offers on products without a set price, so unless the page also has review or aggregateRating markup, it no longer qualifies. The product schema guide and the Article and BlogPosting guide list the recommended properties worth adding. Google's introduction to structured data also says "it is more important to supply fewer but complete and accurate recommended properties" than to fill every field with weak data.
Multiple conflicting blocks from plugins and themes
Having several JSON-LD blocks on one page is normal. Google's guidelines say "Google Search understands multiple items on a page, whether you nest the items or specify each item individually", and the JSON-LD 1.1 specification says that, unless one specific script is targeted, every application/ld+json block on a page is processed and merged into a single dataset. The problem is when two blocks describe the same thing differently.
It tends to happen on WordPress and Shopify sites, where an SEO plugin, the theme and a reviews or e-commerce app can each print their own Organization or Product. A typical result: two Organization blocks with different names and logos, or two Product blocks where one carries a cached price. Merging does not settle the conflict, and neither Google's documentation nor the specification says which value a consumer will use.
To fix it:
List every block with the curl command above and note which tool printed each one (plugin comments or class names in the HTML usually say).
Choose one source per type and switch schema output off in the others. Most SEO plugins and many themes have a setting for this.
Give shared entities a stable @id and reference it instead of repeating the object.
This is the error validators cannot catch, because the markup is valid. Google's guidelines say "Your structured data must be a true representation of the page content" and "Don't mark up content that is not visible to readers of the page" (Google). Pages that break those rules risk a structured data manual action, which Google says removes rich result eligibility but does not affect web ranking.
Common mismatches:
Price: the page shows a sale price of $39; the markup still says "price": 49.
Availability: the button says "Sold out"; the markup says https://schema.org/InStock.
Dates: the article was updated last week; dateModified is from two years ago, or is set to today on every request.
Names and ratings: the markup claims an aggregateRating from reviews that appear nowhere on the page.
To check, pick a handful of key values (name, price, availability, dates, address) and compare the JSON-LD with the visible text, ideally on pages with sales, variants or recent edits. The fix is structural: generate the markup from the same data, in the same render, as the visible content, so it cannot be cached or updated separately.
Frequently asked questions
Does one JSON-LD error break the whole page?
A syntax error breaks the block it is in, because a JSON parser rejects the whole block. Other valid blocks on the page and the page's normal content are unaffected, except when an unescaped </script> ends the block early and spills the rest of it into the visible page.
Should @context use http or https?
Either works. schema.org's FAQ says both https://schema.org and http://schema.org are fine and that http-based URLs will remain widely understood.
Is it a problem to have several JSON-LD blocks on one page?
No, as long as they do not contradict each other. Google reads multiple items whether they are nested or separate, so the risk is two blocks giving different names, logos or prices for the same thing.
Does valid JSON-LD help a page appear in AI answers?
Not in any way the operators document. Google says no special schema.org markup is needed for AI Overviews or AI Mode, and other AI search operators do not document how they use JSON-LD. Valid, accurate markup removes one source of confusion; it does not earn citations on its own.
Why does the Rich Results Test pass when my page source shows no JSON-LD?
Google can read JSON-LD added by JavaScript, so its tools see the rendered page. Check the raw HTML with curl to see what crawlers that do not run JavaScript receive.
Next step
Run the curl command above on your most important templates first: a product, an article and the homepage. To check a single page without an account, run the free check. For a whole site, an audit runs deterministic checks across up to 500 pages, adds human review, and delivers a PDF with the evidence and prioritized fixes.
Filed under Structure and schema. Topics: · json-ld, structured-data, troubleshooting, wordpress, javascript
What structured data does for AI search, what Google and AI operators actually document, which JSON-LD types matter, and how to test that your markup is right.
How to write Article schema in JSON-LD: headline, author as a Person with a url, honest datePublished and dateModified, image, publisher and the common mistakes.
A complete Organization schema example in JSON-LD, which LocalBusiness subtype to use, where to put the markup, and the errors that make it contradict itself.