TL;DR
- Merchant Center's "Invalid value for category [price]" fires whenever the price attribute Google receives is exactly 0; the only documented exception is mobile devices sold under a contract promotion.
- On Shopify the zero comes from one of two unrelated causes: an unfilled variant price (a data issue, no code involved) or a theme's JSON-LD snippet reading a variable that can resolve to empty (a code bug that hits every product page).
- Scanning structured data across 90,000+ Shopify product pages, roughly 1 in 5 stores had at least one page emitting a zero price, usually on a small slice of the catalog, which is exactly why spot-checking a few products rarely catches it.
If a client's Shopify product silently drops out of Google Shopping, or picks up a "Invalid value for category [price]" disapproval in Merchant Center, don't start by re-checking the price field in admin. Start with the structured data the page actually serves. Google flags this error when the price attribute it receives is 0, a value it never treats as a real offer. On Shopify that zero rarely shows up where you'd expect it: it's usually buried in one variant nobody filled in, or it's a theme snippet reading a variable that can resolve to empty even when the admin price is correct. Both produce the identical Merchant Center error, and only one of them needs a code fix.
This post covers how to tell which cause you're dealing with on a client's store, and how to fix each.
What does "Invalid value for category [price]" actually mean?
Google's own documentation is blunt about it: the issue "occurs when you've entered 0 for the price [price] attribute." There's exactly one exception, for a mobile device sold as part of a contract or promotion, where a price attribute of 0 is allowed. Outside that case, a price of zero isn't a valid commercial offer, so Google rejects the item outright.
Why does this bug hide from spot-checks?
It's a loud failure with a quiet distribution. It rarely touches a whole catalog; it usually touches a handful of products inside a catalog that otherwise looks fine. Across a scan of more than 90,000 Shopify product pages, roughly one store in five had at least one page declaring a price of zero in its structured data, and in the typical case the affected pages were a small fraction of that store's catalog.
That combination, widespread across stores but nearly invisible within any single one, is why it survives. Opening a few products in admin to eyeball the price almost never lands on the broken one.
Cause 1: is the variant price actually empty?
Someone created the product as a draft with a placeholder price and never filled it in, or added a variant later and left its price blank. Admin is telling the truth here, and the markup is faithfully reporting it. This is the mundane case: no code change needed, just set the price.
The detail that catches people out is that it's usually one variant, not the product. A product whose first variant is priced correctly looks entirely normal at a glance, while a second or third variant sits at zero and drags the whole item into a disapproval.
Cause 2: is the theme reading the wrong price variable?
Here admin is correct and the markup still says zero. The theme's JSON-LD snippet is reading a field that can be empty instead of the one that always resolves to the displayed variant. In Liquid, that means checking whether the snippet uses something like:
"price": "{{ product.selected_or_first_available_variant.price | money_without_currency }}"
rather than a raw product.price, product.variants.first.price, or any field that can be nil when a variant is out of stock or unpublished. product.selected_or_first_available_variant is the one that reliably matches what's rendered on the page.
This version is the one worth flagging in a code review, because it isn't a one-off data mistake, it's a snippet that runs on every product page on the store. It also survives every correction made in admin, which is what makes it confusing to debug from the client side: they fix the price, the price is right, and Google keeps rejecting the item anyway.
How do you tell which one you're looking at?
Run the product URL through the Rich Results Test and read the price field on the detected Product. That's the number Google actually receives, not the one typed into admin.
- Open the same product in Shopify Admin → Product → Pricing and check every variant, not just the first.
- If a variant has no price, that's your answer: set it.
- If every variant price is correct in admin and the markup still shows 0, the bug is in the theme snippet: confirm it reads
product.selected_or_first_available_variant.price.
What's the actual fix, and how do you confirm it?
For the data case, filling in the missing variant price is the entire fix. For the code case, correcting the JSON-LD variable fixes every product on the store at once, so it's worth doing properly rather than patching one product's snippet.
Either way, revalidate with the Rich Results Test before telling the client it's resolved, then let the Merchant Center feed refresh (or reupload manually). Affected items show up under the Needs attention tab once Google has re-crawled the feed.
How do you check an entire client catalog, not just one product?
Merchant Center's Needs attention tab only lists products that already reached a feed. A product that never made it into a feed can carry a zero price in its structured data indefinitely without ever surfacing in that report. If you're auditing a client's store, the structured data actually served on live product pages is the source of truth, not the feed diagnostics. This is worth building into any theme handoff or Merchant Center audit checklist, since it's cheap to check and easy to miss.
It's also worth distinguishing from two related failures: a price mismatch, where the page and the markup disagree on a real price, and a stale price, where the price is valid but out of date. A zero price is neither: the page and the markup usually agree, on a number that can't be right.
Have you run into this on a client's theme before, was it a missing variant price or a bad JSON-LD variable?
The full version of this article — with screenshots and ongoing updates — lives on the StoreCanary blog.
Top comments (0)