← Back to writing
Writing · agentic commerce

Why AI Agents Say You're Out of Stock When You're Not

By Leo Nguyen · Sep 10, 2026 · 8 min read
Why AI Agents Say You're Out of Stock When You're Not
Jump to section

Short version: A US furniture store had 30,855 products sitting in stock and a website that clearly said so. Four AI shopping agents still told buyers it was sold out. The store wasn't unlucky, and the agents weren't broken. The availability the machines read and the availability the humans saw were two different numbers living on the same page. Availability isn't a ranking problem — it's a structured-data problem, and it fails silently.

#Humans and agents read two different versions of "in stock"

Open one of your product pages. You see a green "In stock" badge, a price, an Add to Cart button. That's the DOM — the page after your JavaScript has run in a real browser.

An AI shopping agent almost never sees that. It reads the page as it arrives over the wire: the server-rendered HTML and any JSON-LD Product/Offer block embedded in it — the version that exists before your JavaScript paints anything in. If your "In stock" badge is drawn in by client-side JS, but the raw HTML or the JSON-LD still carries a stale value like BackOrder, the agent trusts what it read first. Then it repeats that to the shopper: "It looks like that's backordered — here are some alternatives."

The agent read the page correctly. The page was telling two different stories to two different readers.

This assumes the agent reached your page at all. If it can't resolve your URL, none of this markup is ever read — that's the redirect-map layer underneath, the prerequisite for everything here. This post is about the next question: once the agent arrives, does it read the truth?

#Where availability actually lives

In structured data, stock status isn't prose — it's a specific field with a controlled value. On a schema.org/Offer, it's availability, and it takes a URL value from a fixed set:

  • https://schema.org/InStock
  • https://schema.org/OutOfStock
  • https://schema.org/BackOrder
  • https://schema.org/PreOrder
  • https://schema.org/LimitedAvailability

That value is what an agent quotes when a buyer asks "can I get this?" And agents read in layers, in order of trust: JSON-LD Product/Offer first, then microdata, then the visible text in the HTML. When those layers agree, everything is fine. When they disagree — JSON-LD says one thing, the on-page text says another — the agent has to guess which one is authoritative, and it often guesses wrong.

This is why "it looks right on screen" doesn't save you. The badge a human sees is the last thing an agent trusts, and usually the thing it never sees at all. If your JSON-LD ships a stale availability, a perfectly correct JavaScript badge is invisible to the machine reading you.

#Three bugs that make availability read wrong

These are mechanism-level patterns — the recurring shapes we see reading public storefronts, not measurements of any one store.

#1. Stock painted by JavaScript, HTML and JSON-LD left stale

The most common one. The server ships HTML (and often JSON-LD) with a default or last-known availability value, and the real status is fetched and painted in by client-side JavaScript after load. Humans get the corrected badge because their browser runs the JS. The agent reads the served bytes and leaves before the JS ever runs. Whatever stale value was in the HTML is the answer it takes away. This is exactly what happened to Home Elegance below.

#2. Duplicate or conflicting JSON-LD

Two Product nodes on one page — one from the theme, one from an SEO app — each with its own Offer. The agent has to resolve which one is real, and there's no guarantee it picks the one with the correct availability. The fix is deduplication: one Product/Offer graph per page, with a stable @id so the node is unambiguous. (This is the same class of markup hygiene that governs brand and AggregateRating resolution, too.)

#3. An unconditional boilerplate sentence in the template

A template that prints "Ships in 4–6 weeks" or "This item is backordered" into the HTML for every product, regardless of real stock, because the condition that should have gated it was never wired up. A human skims past it. An agent reads it as the product's official status and quotes it back — even when your inventory system says the item is sitting on the shelf.

All three are fixable at the markup/render layer. None of them require touching checkout, the theme's design, or your ERP.

#A real case: Home Elegance USA

Home Elegance USA is a US Shopify furniture retailer with 30,855 products, all showing in stock on the site. We tested availability the boring, repeatable way: five buyer questions across four agents — ChatGPT, Gemini, Perplexity, Claude — 20 cells, scored and dated.

  • Baseline: 4 of 4 agents told shoppers that in-stock furniture was backordered or unavailable. 0 of 4 confirmed "In stock."
  • The cause: bug #1 above — a backorder sentence rendered unconditionally in the HTML, with the true stock status painted in later by JavaScript.
  • The fixes, over about ten days: made the backorder sentence conditional and server-rendered the real stock status; deduplicated JSON-LD and re-attached brand via a stable @id; wired live AggregateRating; added FAQ schema. All correctness work, all at the markup layer.
  • After: 0 of 4 agents said backordered; 3 of 4 explicitly confirmed "In stock."

And the part we say out loud on purpose: category ranking stayed flat (roughly 4/12 → 3–4/12). These were correctness fixes, not ranking fixes. Making an agent describe you accurately is a different mechanism from making it pick you in a competitive answer — the second runs on reviews, entity authority, and third-party corroboration over a longer arc. Anyone claiming a ten-day ranking win from a template fix is selling something.

The full case narrative — symptom, cause, five fixes, dated before/after — is here: Four AI Agents Told Shoppers This Furniture Store Was Out of Stock, and the client-approved case page is at luma-e.com/work/home-elegance-usa-ai-agent-commerce-readiness-2026.

#How to know your fix actually worked

Measure it the way you'd measure anything you want to trust: a fixed set of buyer questions, asked across the agents that matter, scored and dated, before and after. Boring, repeatable, dated. If it isn't written down with a date next to it, it isn't a result — it's a vibe.

You can run the smallest version of this at home right now. Pick your best-seller. Ask ChatGPT whether it's in stock, phrased the way a real buyer would ask — "is the [product] available to order?" Then compare the answer to what your inventory system actually says. If they disagree, you've found the gap, and it's almost certainly one of the three bugs above.

#The fix, without a rebuild

  • Server-render the real stock status into the HTML. Don't let client-side JavaScript be the only source of truth for availability. The value in the bytes is the value the agent takes.
  • Deduplicate JSON-LD. One correct Product/Offer node per page, with a stable @id so there's nothing for the agent to resolve wrong.
  • Map Offer.availability to the real state. InStock / OutOfStock / BackOrder / PreOrder where each actually applies — and delete the unconditional backorder sentence.
  • Keep the layers in sync. Price, currency, AggregateRating, and availability should say the same thing in the HTML and in the JSON-LD. Disagreement between layers is what makes an agent guess.

Availability isn't a badge — it's a value the machines read before your customer ever sees your site. Make the version the agent reads match the version that's true. Everything else in "AI visibility" comes after that. If you've never checked, the fastest test is free: ask ChatGPT whether your best-seller is in stock, the way a real buyer would — and see which of your two answers it gives back.


Send one store URL and we'll run a free async AI-correctness read: we ask live AI agents about your products, run the same 20-cell availability test, and send back a named punch-list of exactly which agents are reading your stock wrong and where the markup disagrees with the truth: luma-e.com/ai-readiness. No call required.

Frequently asked
Where does an AI shopping agent actually read whether a product is in stock?
From the page as it arrives over the wire — the server-rendered HTML and any JSON-LD Product/Offer block — not from the interface a human sees after JavaScript runs. In schema.org terms it's Offer.availability, a URL value like InStock, OutOfStock, BackOrder or PreOrder. If your visible 'In stock' badge is painted in by JavaScript after load, but the raw HTML or the JSON-LD still says the product is backordered, the agent trusts what it read first — and repeats it to the shopper.
How is this different from an SEO structured-data problem?
It's the same markup, but the stakes and the timing changed. For SEO, a stale availability value costs you a rich-result badge and gets corrected on the next crawl over days. For an agent, that value is read the moment a buyer asks 'is this in stock?' and becomes the answer the buyer hears — before they ever reach your site. There's no second crawl to save you, and nothing in your analytics records the lost sale.
What are the common bugs that make availability read wrong?
Three recurring ones. First, stock state painted by JavaScript while the server HTML and JSON-LD ship a stale value. Second, duplicate or conflicting JSON-LD — two Product nodes on one page, and the agent resolves the wrong one. Third, a template that prints an unconditional 'ships in X weeks / backordered' sentence for every product regardless of real stock. All three are fixable without rebuilding checkout, theme, or ERP.
How do you know a fix actually worked?
Test it the boring, repeatable way: a fixed set of buyer questions across the agents that matter — ChatGPT, Gemini, Perplexity, Claude — scored and dated, before and after. On a recent US Shopify furniture store (30,855 products), agents saying the in-stock catalog was 'out of stock' went from 4 of 4 to 0 of 4, with 3 of 4 confirming 'In stock' after the fixes. We also report what didn't move — category ranking stayed flat — because these are correctness fixes, not ranking tricks.
NEWSLETTER

More like this, only when it's worth it

Occasional email on AI x ecommerce - how agents read stores, what breaks, what we shipped. No weekly filler.

No spam, no sequence-drip. Unsubscribe anytime.