DEV Community

137Foundry
137Foundry

Posted on

Why Schema Markup Validates Locally But Still Fails Google's Rich Results Test

You run your JSON-LD through a JSON linter. It parses. You check every @type against schema.org's spelling. It matches. You confirm required properties are present. They are. And Google's Rich Results tool still comes back with no eligible features. This is a genuinely confusing state to debug, because every check you know how to run says the markup is correct.

The gap is between "valid" and "eligible"

Schema.org defines a vocabulary. It does not define which markup earns which visual treatment in Google's search results. Those are two separate systems with two separate sets of rules, and a block of JSON-LD can satisfy the first completely while failing the second for reasons that have nothing to do with the JSON itself.

The schema.org vocabulary cares whether your Product object has a name and, ideally, an offers or review. Google's rich results eligibility layer cares about that too, but it also cares about things schema.org has no opinion on: whether the page has enough unique content to be considered non-spammy, whether the domain has a track record that makes Google trust its structured data claims, and whether the specific rich result feature is currently available for your content category and region at all. A perfectly valid Product schema on a thin, mostly-duplicated page can fail eligibility for reasons a JSON validator will never surface.

Content quality signals you can't lint for

Google has been explicit for years that structured data markup alone doesn't guarantee a rich result, and that broader page quality plays into the decision. A FAQPage on a page that's otherwise thin, templated boilerplate with little unique value is a candidate for suppression even with flawless JSON-LD, because the feature is meant to represent genuinely useful content, not a signal to be gamed by attaching valid markup to low-value pages.

This is frustrating from a pure engineering standpoint because it's not testable the way syntax is. There's no lint rule for "is this page good enough." The closest practical proxy is to ask honestly whether the page would earn the rich result on its own merits if a person, not an algorithm, were deciding, and to treat markup as a way of surfacing content that's already strong rather than a way of dressing up content that isn't.

Feature availability isn't universal or permanent

Rich result features roll out incrementally, get tested in specific verticals, and sometimes get retired. A schema type that earned a visual rich result eighteen months ago might not today, not because your markup changed, but because Google changed which features are live for your content category. This is one of the more disorienting failure modes because nothing in your code changed at all, and yet the outcome did. Wikipedia's overview of schema.org covers how the vocabulary itself is jointly maintained across search engines, which helps explain why feature availability isn't something any single engine commits to permanently.

There's no way to lint against this locally, since it's a decision made entirely on Google's side and subject to change without a deprecation notice most teams will see in advance. The practical response is to treat rich result appearance as a bonus outcome of correct markup, not a guaranteed one, and to build your SEO strategy on the underlying content quality rather than on any single visual feature persisting indefinitely.

Indexability rules structured data doesn't override

Structured data on a page blocked by robots.txt, or carrying a noindex directive, gets ignored entirely regardless of how well-formed it is. This sounds obvious written out, but it's a common cause of "why isn't this working" confusion on staging environments, preview URLs, or pages that were intentionally deindexed for an unrelated reason and never revisited. Before debugging the JSON-LD itself, confirm the page is actually indexable in the first place. It's a thirty-second check that rules out an entire category of false leads.

Canonical mismatches quietly redirect the signal

If the URL you're checking in a tool isn't the canonical version of the page, Google may be evaluating structured data against a different URL entirely, one that might have different, older, or missing markup. Confirm the URL in your testing tool matches what's actually set as canonical, especially on sites with parameter-heavy URLs, staging subdomains, or recent URL structure changes, since a stale canonical pointing at an old page is invisible in the JSON-LD itself.

What's actually worth checking, in order

Given all of this, a sane debugging order for "my markup validates but the rich result isn't showing" looks less like re-checking the JSON and more like ruling out everything around it:

  1. Confirm the page is indexable, not blocked, not noindexed.
  2. Confirm the URL being tested matches the canonical URL.
  3. Confirm the content itself is substantial enough to plausibly earn the feature on its own merits, independent of markup.
  4. Confirm the specific rich result feature is currently live for your content type and region.
  5. Only after all of that, revisit the JSON-LD itself for the subtler mistakes, casing, missing nested types, content mismatches, that a basic validator won't catch either.

A pattern that shows up constantly on migrated sites

This gap between valid and eligible shows up especially often right after a site migration or a CMS switch. The new platform generates technically correct schema markup out of the box, often better markup than the old system had, and yet rich results that were showing before the migration don't come back immediately, or come back inconsistently across different pages. Teams read this as a markup regression and spend days re-verifying JSON that was already fine.

What's usually actually happening is a combination of the canonical and indexability issues above: migrations frequently introduce temporary duplicate URLs, staging artifacts that got indexed, or canonical tags pointing at old paths during the transition window. Google's crawlers and evaluation systems need time to re-establish trust signals for the new URL structure, independent of whether the schema itself is correct. The markup was never the problem. The surrounding signals the migration disturbed were.

Why "it worked before" isn't proof the markup is fine now

A rich result that was showing up before a change and isn't showing up after feels like strong evidence the change broke something. Sometimes it did. But it's worth remembering that "it worked before" describes markup that was evaluated against a specific version of Google's eligibility rules, a specific state of your page's content and indexability, and a specific point in that page's trust history with the crawler. Any of those three can shift independently of anything in your codebase, which is exactly why treating a lost rich result as automatically a code regression leads teams to re-debug JSON that was never broken in the first place.

The uncomfortable part

Some of this genuinely isn't fixable through markup at all. If a rich result feature isn't currently available for your content type, no amount of schema refinement changes that, and it's worth being honest with a team or a client about that distinction rather than continuing to iterate on JSON that was never the actual blocker. Knowing which category a problem falls into, markup bug versus eligibility decision versus feature availability, saves a lot of wasted debugging time chasing a fix that doesn't exist.

We go deeper into separating these failure categories, plus a full walkthrough of how to actually read Search Console's vague error messages when there is a genuine markup bug, in 137Foundry's structured data debugging guide. For the base vocabulary reference, schema.org remains the source of truth on what's structurally valid, and Google's Rich Results tool is still the fastest way to check eligibility directly rather than guessing from search results alone.

Top comments (0)