Structured data an agent invented
Agents produce JSON-LD that validates on the first attempt, is schema.org compliant, and describes a price that is not your price and reviews nobody left. Valid syntax around a false claim is the dangerous combination.
Ask an agent to "add structured data for SEO" and you get a block of JSON-LD
that validates cleanly on the first attempt: correct @context, correct
@type, properties nested the way the schema.org documentation nests them,
every required field filled.
Look at the values.
{
"@type": "Product",
"name": "Standard plan",
"offers": { "@type": "Offer", "price": "29.00", "priceCurrency": "USD" },
"aggregateRating": { "@type": "AggregateRating",
"ratingValue": "4.8", "reviewCount": "127" }
}
The plan costs £24 and is billed monthly. There is no USD price. And there are no reviews — there is no review system on the site at all. The 4.8 and the 127 exist because the schema has fields for them and most product pages in the training data have ratings, so the likeliest completion has ratings.
Nothing here malfunctioned. The markup is valid. The generator filled a shape, which is what generators do.
Why a false claim costs more than silence
Structured data is not a formatting hint. It is a machine-readable assertion,
in your own voice, on your own domain, addressed to a search engine. When you
publish "price": "29.00" you are not describing your page. You are telling
Google what the price is. No markup at all is a missed opportunity; markup that
misrepresents the page is a policy violation, and those are not the same
category of mistake.
That distinction has three consequences.
It is used, so it is wrong in public. Structured data populates rich results: the star ratings, the price, the availability, the FAQ accordion in the search listing. A false assertion is therefore displayed to people before they ever reach your site. Someone who clicks a $29 listing and lands on a £24 page has been misled by you, and someone who clicks 4.8 stars and finds no reviews has learned how much to trust the rest of the page.
The consequence is not confined to the page. Google's structured data guidelines prohibit markup that misrepresents the content of the page, and review markup for reviews that do not exist is a named violation. Enforcement takes two forms: rich results can simply stop being generated, and Search Console can report a manual action for structured data. Manual actions state the affected scope, and that scope can be a section or a site rather than one URL. So you do not necessarily get to lose the stars on the one page with the invented reviews and keep them everywhere else. A generated block on one shared template therefore puts every page built from that template at risk, not just the one somebody noticed.
It is invisible in review. A pull request that changes a price in visible
copy gets noticed. One that changes a price inside a <script type="application/ld+json"> block does not: the diff is a config-shaped blob
at the bottom of a template, the rendered page looks identical, and two people
approve it. That is why the class survives: the value is legible to a crawler and
effectively invisible to a reviewer scrolling a diff.
The fields that get filled in with fiction
These recur because they are the fields where a convincing value is easy to produce and a true one requires knowing something.
| Field | What gets fabricated | Why it happens |
|---|---|---|
aggregateRating / review |
Ratings and counts for a site with no reviews | The type has the field; product pages usually carry ratings |
price / priceCurrency |
A number from an old pricing page, or USD on a GBP site | Prices change in one place and the JSON-LD is not that place |
address / telephone on Organization |
A convincing street address, or at least the shape of one | The example in the documentation had an address |
availability |
InStock, hardcoded |
It is the value in every example |
datePublished / dateModified |
The date the markup was written, static thereafter | Nothing updates it, and nothing notices |
FAQPage |
Questions and answers not present in the visible page | The requirement said "add FAQ schema", not "mark up the FAQ" |
author |
A person or organisation that did not write it | The field is required and something had to go in it |
The mandatory fields are the dangerous ones, not the optional: a field that must be filled creates pressure to produce a value, and the agent has no way to answer "I do not know this".
One rule catches most of it at once: structured data must describe content visible on the page. FAQ markup for questions the page does not display violates the guidelines whether or not the answers are true. If the markup asserts something a reader cannot find by scrolling, it is wrong even when it is accurate.
What a person should actually check
Not the syntax. That was the part that was already right.
- Every number, against its source. Price, currency, billing period, rating, review count, stock status, with the page and the markup open side by side.
- Whether the entity exists. Does the organisation have that address, that phone number, that legal name? Did that person write it?
- Whether the rendered page says the same thing. If a claim lives only in the JSON-LD, remove it.
- Whether it is stale.
dateModifiedon a page edited last week.InStockfor something that sold out. A price from before the increase. Nothing reconciles these against the systems they came from, so they decay in silence. - What is duplicated. A CMS plugin's block and an agent's block asserting different prices for the same product, with no obvious survivor.
- Whether you would sign it. Would you put this claim in a sentence, in your own name, on the page? If not, it does not belong in the markup either.
Google's Rich Results Test and the Schema Markup Validator confirm that the markup parses and is eligible. Neither can tell you the price is wrong; nothing can, because nothing outside your organisation knows the price. That check is a person comparing two things. It is worth saying plainly, because the existence of a validator makes people feel the checking has been done.
Where monitoring helps, and where it does not
We should be straight about this, because it would be easy to imply otherwise: HarpyWatch does not validate whether your structured data is true. No external observer can, because nothing outside your own systems knows what your price is supposed to be.
What an external check does is narrower and still useful. dom-content
asserts that a string you specify appears in the rendered page: if the price in
your markup is meant to be the price in your copy, you can be told when that
string stops appearing. broken-links catches url and sameAs fields
pointing at pages that no longer exist — the one fabricated value that decays
into a hard error. browser-render catches markup injected by a script that
has stopped running.
That is the boundary. Monitoring tells you when the observable facts about a page change. It cannot tell you that a fact you asserted was never true.
Which is the broader point. Generated output carries a confidence that is a property of the generator rather than of the world: a model guessing does not emit a scruffier artefact than a model that knows, it emits the same fluent, well-formed, correctly-nested one. So every claim has to be checked against something outside the system that produced it — the database for the price, the register for the address, the rendered page for the content.
Structured data is only where this shows up most starkly, because there you are handing the assertion to a third party who will repeat it to your customers.
Cite this
The HarpyWatch team. “Structured data an agent invented”. The Nest, HarpyWatch, 3 November 2026 UTC. https://harpywatch.com/blog/structured-data-an-agent-invented
More from The Nest
The vulnerabilities agents write fresh
Ask an agent for a login flow and you tend to get a ?next= parameter redirected to whatever it contains. It is the obvious implementation, it is an open redirect, and the remedy was documented years before the model was trained.
What thirty seconds of checking would have caught
A catalogue of small, boring, real failures — a www that resolves nowhere, a redirect that drops to http, a response that is not actually compressed — each one cheap to see from outside.
The certificate an agent provisioned and nobody agreed to renew
An AI agent that ran certbot once did the work correctly and made no commitment about the renewal. The setup is in your repository; the recurring obligation it created is in nobody's calendar.