Quick answer
The product page was 1.79 MB, the body read ran past the request budget, and code that only looked at the body saw an empty response — which looks identical to a page with nothing in it. Six checks read that one response, so one timeout became six confident failures.
A verdict has three states, not two: yes, no, and could not determine. A negative result requires positive evidence — a successful fetch, a real response — and unknowns must not be scored as zero, or you have only renamed the lie.
We build free audit tools and give the results away. That only works if the results are true. On 8 August 2026 we audited our own tools the way we would audit a client’s store, and the first thing we found was our AI-readiness scanner confidently telling a large storefront it was missing structured product data it demonstrably had. Not a rounding error — a flat, red “fail” on one of the highest-weighted checks in the system.
Here is the whole thing: why it said that, how far the damage spread inside a single scan, and the design mistake underneath it that had nothing to do with structured data at all. We are not naming the store.

01 · Symptom
What the report said.
AgentReady grades a public Shopify storefront on 24 checks across five pillars — discoverability, product data, content legibility, policies, and agent endpoints — weights summing to 100. One check asks a simple question: does the product page carry Product JSON-LD, the structured block stating name, price, currency and availability in a form a machine can lift directly?
For this store the answer came back fail. We opened the product page by hand and viewed source. The JSON-LD was right there, well-formed, with a complete offer. Our tool was wrong in the worst possible direction: it was telling a merchant to go and fix something that was not broken.
02 · Cause
The page was 1.79 MB.
The scanner is deliberately gentle. It is read-only, it never logs in, it runs a small number of requests concurrently, and every request has a hard budget of eight seconds. That budget protects the store being scanned as much as the scan: a tool that hangs for a minute on a slow origin is a tool nobody should point at their production storefront.
The store’s product page weighed 1.79 MB of HTML. The obvious part is that big pages take longer. The part that actually bit us is that the abort signal does not only cover the connection — it covers reading the response body. A server can answer in 200 milliseconds and still blow an eight-second budget while the body streams. From the scanner’s point of view the request simply stopped.
And when it stopped, our fetch wrapper did what fetch wrappers do: it caught the abort and returned a normalised, safe, empty result. Not ok. Status zero. Body: an empty string.
An empty response and a page with nothing in it look identical to code that only looks at the body.
The JSON-LD extractor ran over an empty string, found no JSON-LD, and reported exactly what it saw. Every layer behaved correctly. The system still lied.
03 · Blast radius
One fetch, six rules.
This is the part we had not thought through. The scan fetches one product page and then reads that single response for six different things:
- whether Product JSON-LD is present at all;
- whether the offer inside it carries price, currency and availability;
- whether return and shipping terms appear as schema;
- whether the page has a meaningful meta description;
- whether a canonical tag is present;
- what share of images carry alt text.
Together those six are worth 22 of the 100 points. With the response aborted, three of them fail outright and three degrade to partial credit, leaving five points. A single slow fetch was quietly costing a store roughly 17 points of a 100-point score — the difference between a B and a D — and dressing every one of those deductions up as a specific, actionable defect. A seventh check can join the pile if the store publishes its FAQ markup on the product page rather than a dedicated page.
We had built the scan so that a single fragile dependency fanned out into a quarter of the grade, and we had given ourselves no way to notice.

04 · Design flaw
Two states is a lie.
The structured-data check had exactly two possible answers: pass, or fail. Most of our other checks have three — pass, partial, fail — because most real-world signals are matters of degree. This one felt binary. Either the markup is on the page or it is not. That felt like rigour. It was the opposite.
There are three things a scanner can know about a signal, not two:
- Positive evidence. We fetched the page and the markup is there.
- Negative evidence. We fetched the page and the markup is not there.
- No evidence. We never got the page.
Collapse the third into the second and you have built a machine that reports its own failures as your failures. Every audit tool has this property, and most of them ship without noticing: uptime monitors that call a network blip an outage, accessibility scanners that flag an image that never loaded, SEO crawlers that report a page as missing a title because the crawl budget ran out. The tool is not lying on purpose. It simply has no word for “I could not check.”
A scanner without an unknown state will eventually say no when it means I don’t know — and it will say it with total confidence.
05 · The change
What replaces it.
The redesign has three parts, and only the first is about JSON-LD.
A third state. Every check that depends on a fetched document can now return unknown, carried with the reason — timed out, blocked, never responded. In the report it renders as “could not check” with the reason attached, visually distinct from a red result, and it never generates a fix instruction. If we do not know, we say we do not know.
Evidence, not absence. A rule may only return a hard fail when the underlying fetch actually succeeded. The response’s health is checked once, in one place, before its contents — rather than re-derived by six rules that each assume the document arrived.
A budget that fits the job. The product page is the most information-dense request in the scan and deserves a longer allowance than a 400-byte robots file. There is still a ceiling — a scan that takes a minute is a scan nobody finishes — so the honest resolution for a genuinely enormous page is not an infinite timeout, it is an unknown that says so.
06 · Scoring
The honest denominator.
The subtle question is what an unknown does to the score, and there is a wrong answer in each direction. Score it as zero and you have not fixed anything — you have renamed the lie. Score it as full marks and you have handed every slow, bloated storefront a free pass, which punishes exactly the merchants who did the work.
So an unknown leaves the calculation entirely — out of the numerator and out of the denominator — and the report states how many checks actually completed. A store assessable on 22 of 24 checks gets a score computed on 22 and is told so on the page. The number stays comparable and stops pretending to a completeness it does not have.
It is also a better product. “93 out of 100, based on 22 of 24 checks — two could not be read within the time budget, and here is which” is a more useful sentence than a confident 76.
07 · Practice
The rule we hold ourselves to.
We publish this partly because it is a good story against ourselves and partly because it is the single most common defect we find in other people’s measurement stacks. If you run any tool that produces a verdict — a monitor, a linter, a data quality job, a fraud rule, an inventory sync — ask it one question: what does it do when it cannot get an answer? If the answer is “the same thing it does when the answer is no,” you have this bug.
It is worth saying plainly that this began as a build log, not a changelog: the diagnosis and the design were written while the change was being made. The fix described in section 05 shipped on 8 August 2026. If you scan a very large store and a check you know passes comes back failed, that is this bug — send us the domain and we will show you the raw fetch result rather than argue with you about it.
The scan itself is free, needs no signup, and reads only public pages: scan.googogogo.com. If you want the reasoning behind the 24 checks rather than the story of one of them breaking, we wrote that up separately in what an AI shopper actually sees.
Quick check
Does your tooling know how to say “I don’t know”?
- Every verdict has three states, not two: yes, no, and could not determine.
- A negative result requires positive evidence — a successful fetch, a real response.
- You know how many downstream conclusions rest on any single request.
- Timeouts cover the body read, not just the connection — and you have tested a huge document.
- An undetermined check is excluded from the score, not silently counted as zero.
Free tools have to be right.
A paid audit that is wrong gets argued about in a meeting. A free audit that is wrong quietly sends a stranger off to fix something that was never broken, and they never tell you. That asymmetry is why we would rather publish a bug in our own scanner than let it sit.
Questions
Questions people ask.
Why did the scanner report Product JSON-LD missing when it existed?
Because the page was very large, the body read timed out, and the check treated an empty response the same as a page with no markup. The scanner said “no” when the honest answer was “I could not get the page”.
What is the three-state rule for audit tools?
Every check answers yes, no or could not determine. A tool without an unknown state will eventually say no when it means it does not know — and it will say it with total confidence.
How should unknown results affect a score?
Not as zero, which only renames the lie, and not as a pass. Unknowns need an honest denominator so the score reflects what was actually measured, and the report should say how many conclusions rest on a single request.