Your Structured Data Passes Google's Test and Is Still Wrong

Key takeaways
- Google's Rich Results Test validates the properties its own features consume. Properties outside that set are ignored, not checked — so invalid markup routinely reports as passing.
- Four vocabulary errors shipped on every page of this site and passed every Google tool: Person.founder, Person.areaServed, Organization.geo and OfferCatalog.provider. None of those properties is permitted on the type it was attached to.
- The check that catches this is mechanical: expand your JSON-LD, then for every property confirm the node's type appears in that property's schema.org domainIncludes list.
- A homepage BreadcrumbList whose items are all on-page anchors is not a hierarchy and is discarded — emitting nothing is better than emitting an invalid trail.
- SoftwareApplication without offers, review or aggregateRating is an error in Google's Software App feature. If you have no honest price or rating to state, use a type that is not in that pipeline rather than inventing one.
The short answer
Google's Rich Results Test validates the properties its own search features consume, and ignores everything else in your markup. So a page can carry structured data with genuine schema.org vocabulary errors — properties applied to types that do not permit them — and the test will report a clean pass. The errors are real, the affected nodes are dropped, and no Google tool tells you.
I know this specifically rather than theoretically, because it was true of this site. Four vocabulary violations were shipping on all 212 pages, emitted by the shared builder that generates the JSON-LD @graph. Everything passed. They were only found by validating the emitted graph against schema.org's own published vocabulary file.
What a passing test actually means
The Rich Results Test answers "does this page qualify for the search features I support?" It does not answer "is this markup valid?" Both are useful questions. Only one of them is the question most people think they are asking.
Why the two checks diverge
Structured data has two independent layers of correctness, and tooling has historically only made one of them easy to check.
The first layer is syntactic: is it parseable JSON-LD, are the @id references resolvable, are dates in a format a machine can read. Almost everything checks this, and almost nothing fails it, because a broken brace breaks the whole block visibly.
The second layer is the vocabulary itself. Schema.org publishes, for every property, a list of the types that property is permitted on — the domainIncludes attribute. A property outside that list is not "unusual" or "unsupported", it is invalid: the consuming system has no definition for what that combination is supposed to mean. This is the layer that goes unchecked, because verifying it requires actually reading the vocabulary rather than a feature spec.
The four errors that shipped here
Each of these was written in good faith, states something true about the business, and is invalid. That combination is the whole problem — none of them looks wrong when you read the JSON.
| What was emitted | Why it is invalid | The valid way to say it |
|---|---|---|
| Person.founder → Organization | founder is defined on Organization, not Person. The property points from the company to the human, never the reverse. | Keep Organization.founder → Person. The fact is already stated correctly in that direction. |
| Person.areaServed → [Country, …] | areaServed is not permitted on Person. Its domain is ContactPoint, Demand, Offer, Organization, Service and two others. | State it on the Person's ContactPoint, and on the Organization — both of which permit it. |
| Organization.geo → GeoCoordinates | geo belongs to Place. An Organization is not a Place; it has one. | Add Organization.location → Place, and put geo on the Place. |
| OfferCatalog.provider → Person | OfferCatalog is an ItemList. provider is defined on Service, CreativeWork, Action and similar — not on lists. | Put provider on each Service inside the catalog, where it is both valid and more precise. |
Notice what these have in common. Every one is a case of attaching a property to the entity a human would naturally attach it to. A consultant does serve areas. A company does have coordinates. A list of services does have a provider. The vocabulary simply models each of those relationships through a different node, and it is unforgiving about it.
How to check it properly
The check is mechanical enough to automate, and worth automating precisely because it is the kind of thing nobody does by hand twice.
- Fetch the page and extract every application/ld+json block. Parse each one — a block that fails to parse is invisible to search engines entirely, and this is the only failure mode that is genuinely silent in production.
- Flatten the @graph into individual nodes, keeping the path to each so an error can be reported somewhere findable.
- For each node, resolve its @type up the subclass chain. A property valid on CreativeWork is valid on BlogPosting, and a checker that does not walk the hierarchy will produce false positives on almost every real graph.
- For each property on the node, look up its domainIncludes list in the schema.org vocabulary and confirm at least one entry appears in that resolved ancestor set. If none does, the property is invalid on that type.
- Collect every @id referenced but never defined. Dangling references are not vocabulary errors, but they mean a node is pointing at something that does not exist in the graph — usually a typo in an anchor, and usually the reason two entities never got connected.
The one deliberate exception worth encoding: Google's sitelinks searchbox uses a query-input property on SearchAction that is not in the schema.org vocabulary. It is documented by Google and correct to keep. A strict checker will flag it, and it should be allowlisted rather than removed.
Run the hierarchy walk, or expect noise
Most hand-rolled validators skip subclass resolution and then flood you with false positives on properties inherited from Thing and CreativeWork. A checker you stop trusting is worse than no checker, because it converts a real signal into background noise.
If you would rather not build this, Website Validator runs the vocabulary check as part of a wider audit — it parses JSON-LD, expands it to real schema.org IRIs, and checks it against the vocabulary rather than against one search engine's feature list. It also separates that from a rich-results view, which is the distinction this whole post is about. Its checks are deterministic and each finding carries the evidence that triggered it, which matters here: "invalid" is a claim you should be able to trace to a line in the vocabulary, not accept because a tool said so.
Two errors that are about Google, not the vocabulary
Separate from vocabulary validity, there are feature-level requirements that produce genuine errors in Google's own reporting. Two came up in the same pass here.
SoftwareApplication needs a price or a rating
Google's Software App feature treats offers, review or aggregateRating as required. Three products were marked up as SoftwareApplication with none of them, because there is no published price or rating for those products to state honestly. The tempting fix is to emit an offer with a price of zero. That is a false statement about a paid product, and it is the kind of thing that is trivially contradicted by the product's own pricing page.
The honest fix was to stop entering a rich-result pipeline the entities cannot satisfy: they are now typed as WebApplication with operatingSystem and browserRequirements, still resolvable as entities, still owned by the Organization, and no longer failing a feature they were never eligible for.
A homepage breadcrumb that was not a hierarchy
The homepage emitted a five-item BreadcrumbList whose items were its own section anchors — Home, About, Expertise, Work, Contact. Every item resolved to the same page. That is a navigation menu described as a hierarchy, and Google discards it. It now emits no BreadcrumbList at all on the homepage, which is the correct amount of breadcrumb for a page with nothing above it.
Why this matters more for AI answer engines than for rich results
If your only interest in structured data is star ratings in search results, invalid properties outside those features cost you little. That calculation changes when the consumer is a language model deciding who an entity is.
Rich results need a handful of properties. Entity resolution needs the graph — who this person is, which organisation they founded, where they operate, what they offer, and which external profiles refer to the same identity. Those are exactly the assertions that live in the properties no rich-result feature reads, which is to say: exactly the assertions nobody is validating.
An invalid node is dropped in silence. Nothing warns you, traffic does not visibly fall, and the fact you were trying to establish just never arrives. On this site, the dropped facts included where the work is delivered and the link from the person to the company — the two things the markup existed to say. The related reading on entity SEO beyond schema covers what those assertions are for once they do arrive.
What to do this week
- Validate one page against the schema.org vocabulary, not against a rich-results tester. If your markup comes from a shared builder, one page tells you about all of them.
- Check every @id you reference actually exists in the graph you emit.
- Delete any BreadcrumbList whose items do not describe distinct pages.
- Find every node that entered a Google feature pipeline it cannot satisfy, and either satisfy it honestly or change the type.
- Put the vocabulary check in CI. These errors arrive through the template, so they arrive everywhere at once — and they are invisible in every dashboard you already watch.
None of this is glamorous work and none of it produces a chart that goes up next week. It is the difference between markup that asserts things and markup that only appears to.
Frequently asked questions
Does invalid structured data hurt rankings?
Not as a direct penalty, and anyone telling you otherwise is overstating it. The cost is quieter: an invalid node is dropped, so whatever it asserted simply never reaches the knowledge graph. If the dropped node was the one connecting your Person to your Organization, or stating where you operate, you have not been penalised — you have just been silently unheard on the exact facts you were trying to establish.
Why does the Rich Results Test pass markup that is invalid?
Because it is not a validator, despite being used as one. It answers a narrower question: does this page qualify for the specific search features Google supports? To answer that it reads the properties those features require and ignores everything else. A property that is invalid for its type but irrelevant to any supported feature is not in scope, so it produces neither an error nor a warning.
What is the difference between the Rich Results Test and the Schema Markup Validator?
The Rich Results Test checks eligibility for Google features. The Schema Markup Validator, which Google handed to the schema.org community, checks the markup against the vocabulary itself and is the one that reports vocabulary problems. They answer different questions, and passing one tells you nothing about the other. Most teams only ever run the first.
Is areaServed valid on a Person?
No. Schema.org lists areaServed as permitted on ContactPoint, DeliveryChargeSpecification, Demand, FinancialIncentive, Offer, Organization and Service. Person is not among them. It is a natural thing to want to say about a freelancer or consultant, which is exactly why it gets written — but the vocabulary expresses it through the person's ContactPoint, or through the Organization they work for.
Should the homepage have a BreadcrumbList?
Usually not. A breadcrumb describes the path of pages leading to the current one, and the homepage is the root of that path — there is nothing above it. Emitting the homepage's own section anchors as trail items produces a list of entries that all resolve to the same URL, which is not a hierarchy and gets discarded. No breadcrumb is a better outcome than a rejected one.
How often should structured data be re-validated?
Whenever the code that generates it changes, which on most sites means whenever anything changes — because the markup is usually produced by one shared builder for every page. That is also why these errors are worth catching: a single bad property in a shared template is not one broken page, it is every page at once.
Get your structured data actually checked
Send me a URL and I will tell you what your @graph asserts, which of it the vocabulary permits, and which of it every tool you have run so far silently ignored. If it is clean, that is what I will tell you — it takes about ten minutes to find out either way.
Direct: +977 9846162626 · lamichhanesapun2@gmail.com
This post supports the frameworks documented in full on the Authority page.
Related reading