All fix guides
WarningAI & Answer Engines

How to Fix Broken JSON-LD Schema Markup

Your page has structured data that's broken in one of three ways: the JSON-LD doesn't parse at all, it's missing a property Google requires for that schema type, or the same @type is declared more than once with conflicting data. Broken schema quietly forfeits rich-result eligibility, and in the duplicate case it can feed AI answer engines two different answers about the same fact. This is a warning: it won't tank your rankings, but it's a fast, high-value fix once you know which of the three problems you have.

What this means

This guide covers three distinct problems that our audit groups under one heading because they share a root cause: schema markup that exists but doesn't do its job. invalid_schema_json means the JSON-LD in a <script type="application/ld+json"> block fails to parse as valid JSON: a syntax error, not a content problem. missing_required_schema_field means the JSON parses fine and the @type is a real schema.org type, but it's missing a property Google's documentation lists as required for that type to qualify for a rich result. duplicate_schema_type means the same @type (commonly Organization, Product, or FAQPage) is declared more than once on the page, usually with different values in each copy.

Invalid JSON is almost always mechanical. The most common causes are single quotes instead of double quotes around keys and string values, a trailing comma after the last property or array item, an unescaped quote or newline inside a string value, or an HTML comment left inside the script block. All four are valid in a JavaScript object literal, which is why they slip through when someone copies a JS snippet into a JSON-LD block, but none of them are valid JSON. When a parser hits one, it doesn't salvage part of the block; it discards the entire script tag's worth of structured data and moves on.

Required fields differ by type, and the differences are specific enough that guessing is a bad strategy. Product has a real, documented required set: name, plus at least one of offers, review, or aggregateRating fully filled in. LocalBusiness needs name and a complete address. Event needs name, startDate, and location. Article is the one that surprises people: Google doesn't list any properties as strictly required for Article, NewsArticle, or BlogPosting. headline, image, author, and datePublished are recommended, not required, so a missing one is a quality gap, not the same kind of hard failure as a Product with no offers, review, or aggregateRating at all. If our audit flags a missing field on an article-type page, it's worth closing but isn't proof the page is broken. Google's own Search Central documentation for each type is the actual source of truth, since the required-versus-recommended split is type-specific and does change.

Duplicate types usually come from stacking. An SEO plugin auto-generates Organization or WebSite schema, and a theme, a page builder, or a manually pasted snippet from an old tutorial adds a second, separate declaration of the same type with different name, logo, or URL values. A common variant is several FAQPage blocks on one page, each covering a different subset of the visible questions, where a parser has no way to know they should be merged rather than treated as competing answers to the same question.

Why it matters

The direct cost is rich-result eligibility, not rankings. Google has been explicit that invalid or unused structured data doesn't cause a ranking penalty; it simply means the page doesn't qualify for whatever visual enhancement that type earns: a Product's price and availability, a Review's star rating, a Recipe's cook time, and so on. Search Console's Enhancements reports list affected pages under invalid or non-critical items, and that list is worth checking periodically since these errors are otherwise invisible on the page itself.

Whether broken schema is actually worse than no schema depends on which of the three problems you have. A pure syntax error is functionally the same as having none: the block is silently discarded, full stop. Duplicate or conflicting type declarations are the case where it turns into an active liability rather than a neutral one. When two Organization or Product blocks disagree, Google, or any other consumer of the markup, has to pick one or attempt to merge them, and there's no guarantee it picks the current, correct one. Showing a stale price, an old rating count, or the wrong organization name with the implied authority of structured data is worse for a user than showing no rich result at all.

The AI-answer-engine angle is where accuracy matters most, because these systems increasingly treat JSON-LD as a higher-trust source than prose. A well-formed schema block hands a model an explicit, unambiguous fact: this is the name, this is the price, this is the author, this is the organization. When the JSON fails to parse, the model gets nothing from that block and falls back to inferring facts from body text, a weaker source but at least a single one. When two conflicting blocks both parse successfully, the model has two candidate facts and no signal for which is authoritative; it may pick either, or blend them into something that matches neither. That's a genuinely worse outcome than having no structured data, because it manufactures a wrong answer with the appearance of machine-verified precision.

The fix that prevents most of this from recurring is structural: consolidate your page's structured data into one @graph with @id references between entities, instead of letting multiple plugins, templates, or snippets each declare their own copy of the same thing. It's a one-time change in how you generate schema, and it makes duplicate_schema_type largely impossible going forward.

How to fix it

  1. 1

    Work out which of the three problems you actually have

    Paste the raw contents of each <script type="application/ld+json"> block into a plain JSON linter first; if it doesn't parse, you have invalid_schema_json and nothing else on this page will validate until that's fixed. If it parses, run the page through Google's Rich Results Test to catch missing_required_schema_field errors for any type eligible for a Google rich result. Separately, run it through the Schema.org validator at validator.schema.org, which checks vocabulary and structure correctness regardless of whether Google grants that type a visual rich result, and will surface duplicate_schema_type issues the Rich Results Test may not flag. Google also narrowed what the Rich Results Test checks in 2026 (FAQ testing was dropped that June), so the Schema.org validator is now the more complete general-purpose check.

  2. 2

    Fix invalid JSON at the syntax level

    Convert single quotes to double quotes around every key and string value, remove every trailing comma before a closing brace or bracket, and make sure any quote or newline inside a string value is escaped. Delete HTML- or JS-style comments from inside the script block; they aren't valid JSON even though some browsers tolerate them. The more durable fix is to stop hand-writing JSON-LD as a string at all: generate it from your CMS data or a small template function and serialize it with JSON.stringify, which makes this class of error structurally impossible.

  3. 3

    Add the properties Google actually requires for that type

    Look up the required and recommended properties for your specific @type in Google's Search Central structured data documentation; they differ by type, and Google distinguishes errors (missing required properties, which block eligibility) from warnings (missing recommended properties, which just cap how good the result can look). While you're in there, double-check that every value in the markup matches what's actually visible on the page. The Rich Results Test doesn't verify that, and mismatched markup can trigger a manual action even when the test shows a clean pass.

  4. 4

    Consolidate duplicates into one @graph

    Find every source adding schema for the same entity: your SEO plugin, your theme, your page builder, and any manually pasted script tags. Keep one authoritative declaration per @type and remove or disable the others. If different tools legitimately need to reference the same entity, for example a WebSite block referencing your Organization as its publisher, use a single @graph array with an @id on the canonical declaration, and have every other reference point to that @id instead of redeclaring the entity.

  5. 5

    Re-validate and check Search Console after reindexing

    Re-run both the Rich Results Test and the Schema.org validator to confirm a clean pass. Then check Google Search Console's Enhancements reports for the relevant type over the following days to weeks; indexing and reprocessing aren't instant, so a page can still show old invalid-item counts for a while after you've actually fixed it. Re-run your SEO audit once Search Console reflects the fix to close the loop.

Example

<!-- WRONG: single quotes and a trailing comma are valid in a JavaScript
     object literal but not in JSON, so this whole block fails to parse -->
<script type="application/ld+json">
{
  '@context': 'https://schema.org',
  '@type': 'Product',
  'name': 'Wireless Mouse',
  'offers': {
    '@type': 'Offer',
    'price': '24.99',
    'priceCurrency': 'USD',
  },
}
</script>

<!-- RIGHT: double-quoted keys and strings, no trailing commas, and the
     properties Google's Product rich result actually requires -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Wireless Mouse",
  "image": "https://example.com/images/mouse.jpg",
  "offers": {
    "@type": "Offer",
    "price": "24.99",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock"
  }
}
</script>

<!-- One @graph with @id references, so Organization and WebSite are each
     declared once and linked, instead of risking a second conflicting copy -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://example.com/#organization",
      "name": "Example Co",
      "url": "https://example.com"
    },
    {
      "@type": "WebSite",
      "@id": "https://example.com/#website",
      "url": "https://example.com",
      "publisher": { "@id": "https://example.com/#organization" }
    }
  ]
}
</script>

Same Product markup, broken then fixed: single quotes and a trailing comma are enough to invalidate the whole block. The @graph example shows how @id references stop Organization or WebSite from being declared twice with conflicting data.

Platform-specific steps

WordPress (Yoast SEO / Rank Math)

Both plugins generate valid schema on their own, so breakage usually comes from something added on top: a manually pasted script tag from an old tutorial, a page-builder module (Elementor, Divi) with its own schema widget, or a theme that also outputs Organization data. Check Appearance > Theme Editor and any custom HTML blocks for a second <script type="application/ld+json"> tag, and remove or disable whichever source shouldn't own that entity.

Shopify

Schema usually lives in theme.liquid or a dedicated schema snippet file, sometimes duplicated by a JSON-LD app installed alongside a theme that already outputs its own. Check both the theme code and your installed apps for overlapping Product, Organization, or BreadcrumbList output, and disable one source so each entity is declared once.

Wix / Squarespace

Both platforms generate limited schema automatically and rarely conflict with themselves, so duplicates here usually come from a Custom Code or Code Injection block that adds a second Organization or Product declaration on top of the built-in one. Check Settings > Custom Code (Wix) or Settings > Advanced > Code Injection (Squarespace) for any manually added JSON-LD before assuming the platform itself is at fault.

Next.js / raw HTML / headless CMS

Build the JSON-LD object in code and pass it through JSON.stringify before rendering it into the script tag, rather than concatenating template strings by hand; this eliminates invalid_schema_json by construction. Keep one function or component responsible for each entity type so two different parts of the app can't both decide to render an Organization block.

Frequently asked

Is invalid schema markup actually worse than having no schema at all?

It depends which problem you have. A pure syntax error is functionally the same as no schema: parsers discard the whole block and move on. Duplicate or conflicting @type declarations are the case that's genuinely worse than nothing, because Google or an AI crawler has to pick between two disagreeing answers about the same fact (a price, a rating, an organization name) and there's no guarantee it picks the correct one.

Does broken schema markup hurt my Google ranking?

No, not directly. Google has said invalid or unused structured data doesn't trigger a ranking penalty. What it costs you is the rich-result enhancement tied to that data, and Search Console will list the affected pages under Enhancements as invalid or non-critical items.

What's the difference between the Rich Results Test and the Schema.org validator?

The Rich Results Test checks whether your markup qualifies for a specific Google Search visual enhancement, and only for types Google currently grants one to; that coverage has narrowed over time. The Schema.org validator at validator.schema.org checks whether your JSON-LD is syntactically valid and correctly uses schema.org vocabulary, regardless of whether Google does anything visual with it. Use both.

Why does my page have two conflicting Organization or Product schema blocks?

The most common cause is stacking: an SEO plugin auto-generates one, and a theme, page builder, or a manually pasted snippet adds a second with different data. Consolidate into a single @graph, or disable one of the sources, so there's exactly one declaration of that entity, referenced by @id from anywhere else it's needed.

Does your site have this issue?

Run a free, AI-powered audit and we’ll flag this and 150+ other checks in about a minute. No signup.

No signup needed. Results in under 60 seconds.

Related fixes