/ ← Schema Debugger Fixes / Product Schema Errors
In this guide
  1. Two rich results from one Product
  2. The fields Product needs
  3. The errors you will see, and what each one means
  4. Price, currency and availability
  5. Variants, price ranges and several sellers
  6. Fixing JSON that cannot be read
  7. The fix, step by step
  8. Stopping it from coming back
  9. Questions and answers

Two rich results from one Product

Product is the schema.org type for anything you sell: a physical item, a digital download or a product variant. Google reads it for two separate rich results, and the Rich Results Test and Search Console report them separately:

Product snippetsMerchant listings
What it showsPrice, availability and stars under an ordinary search result.Shopping-style results: image, price, delivery, returns, in the Shopping tab, image search and product panels.
For which pagesAny page about a product, including reviews and comparisons.Only pages where the reader can buy the product from you.
Requiredname, plus one of offers, review or aggregateRating.name, image, and offers with price and priceCurrency.
Search Console reportProduct snippetsMerchant listings

A review site that writes about a product it does not sell should aim for product snippets only. A shop should aim for both. When a shop page fails merchant listings but passes product snippets, the gap is almost always image, a missing or badly formatted price, or the shipping and returns fields.

💡 In the Rich Results Test, open both "Product snippets" and "Merchant listings" in the detected items list. An error can appear under one and not the other.

When Product is the wrong type

The fields Product needs

FieldNeeded forWhat it holds
nameBoth (required)The product name as shown on the page.
imageMerchant listings (required)One or more full https addresses of product photos Google can crawl.
offersMerchant listings (required); snippets (one of three)An Offer, or an AggregateOffer for a price range from several sellers.
offers.price and offers.priceCurrencyMerchant listings (required)A number such as 34.00 and a code such as GBP.
offers.availabilityRecommendedA schema.org URL: https://schema.org/InStock, OutOfStock, PreOrder, BackOrder.
offers.shippingDetailsMerchant listings (recommended)An OfferShippingDetails: cost, destination and delivery time.
offers.hasMerchantReturnPolicyMerchant listings (recommended)A MerchantReturnPolicy: return window, method and fees.
brand, sku, gtin, mpnRecommendedIdentifiers that let Google match the product to the same item elsewhere.
aggregateRating, reviewSnippets (one of three)Real ratings and reviews collected on your own site and shown on the page.
descriptionRecommendedA plain-text description of the product.

A correct block you can copy

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Oak chopping board, large",
  "description": "Solid oak end-grain chopping board, 45 x 30 x 4 cm, oiled and ready to use.",
  "image": [
    "https://example.com/images/oak-board-1x1.jpg",
    "https://example.com/images/oak-board-4x3.jpg",
    "https://example.com/images/oak-board-16x9.jpg"
  ],
  "sku": "NW-OAK-L",
  "gtin13": "5012345678900",
  "brand": { "@type": "Brand", "name": "Northwood" },
  "offers": {
    "@type": "Offer",
    "url": "https://example.com/oak-board",
    "price": "34.00",
    "priceCurrency": "GBP",
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition",
    "shippingDetails": {
      "@type": "OfferShippingDetails",
      "shippingRate": { "@type": "MonetaryAmount", "value": "3.95", "currency": "GBP" },
      "shippingDestination": { "@type": "DefinedRegion", "addressCountry": "GB" },
      "deliveryTime": {
        "@type": "ShippingDeliveryTime",
        "handlingTime": { "@type": "QuantitativeValue", "minValue": 0, "maxValue": 1, "unitCode": "DAY" },
        "transitTime": { "@type": "QuantitativeValue", "minValue": 1, "maxValue": 3, "unitCode": "DAY" }
      }
    },
    "hasMerchantReturnPolicy": {
      "@type": "MerchantReturnPolicy",
      "applicableCountry": "GB",
      "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
      "merchantReturnDays": 30,
      "returnMethod": "https://schema.org/ReturnByMail",
      "returnFees": "https://schema.org/FreeReturn"
    }
  }
}
</script>

The errors you will see, and what each one means

MessageCauseFix
Either "offers", "review", or "aggregateRating" should be specifiedA Product with a name and nothing else, often from a theme that outputs a bare Product on every page.Add the offer, or remove the Product block from pages that do not sell it.
Missing field "image" (merchant listings)No image, or a relative path.Add full https image addresses.
Missing field "price" / Invalid priceThe price is empty, contains a currency sign ("£34"), a thousands comma ("1,299.00") or a decimal comma ("34,00").Write 34.00 or 1299.00: digits and a full stop only.
Missing field "priceCurrency" / Invalid currencyNo currency, or a sign or word ("£", "pounds") in place of the code.Use the ISO 4217 code: GBP, EUR, USD.
Invalid value for field "availability"Plain text such as "In stock" or "instock".Use the schema.org URL: https://schema.org/InStock.
Missing field "shippingDetails"No shipping information in the offer (a warning for merchant listings).Add an OfferShippingDetails, or set shipping at account level in Merchant Center.
Missing field "hasMerchantReturnPolicy"No returns information (a warning for merchant listings).Add a MerchantReturnPolicy to the offer or to your Organization.
Invalid object type for field "brand"Brand written as a bare string in some templates, or as an Organization with no name.Use { "@type": "Brand", "name": "..." }.
Duplicate ProductThe platform, the theme and a review app each output their own Product.Keep one complete Product and switch off the others, or merge them with the same @id.

Price, currency and availability

Price problems cause more failed merchant listings than anything else. The rules are strict because Google compares the markup with the price on the page and in your product feed.

WrongRightWhy
"price": "£34.00""price": "34.00"The currency goes in priceCurrency, not in the price.
"price": "1,299.00""price": "1299.00"No thousands separators.
"price": "34,00""price": "34.00"A full stop for decimals, whatever the site's language.
"price": "from 20"AggregateOffer with lowPriceA range needs AggregateOffer.
"priceCurrency": "£""priceCurrency": "GBP"A three-letter ISO 4217 code.
"availability": "In stock""availability": "https://schema.org/InStock"Availability is a fixed list of schema.org values.

Sale prices

Mark up the price the buyer pays now. If the page shows a sale price with the old price crossed out, price is the sale price. Google supports showing the old price through a priceSpecification with "priceType": "https://schema.org/StrikethroughPrice"; do not put the old price in price.

Prices with and without VAT

For UK shoppers, mark up the price including VAT, the same as the page shows. A trade site that shows prices before VAT should add a UnitPriceSpecification with valueAddedTaxIncluded set to false.

Out-of-stock products

Keep the Product and Offer, and change availability to https://schema.org/OutOfStock. Removing the offer altogether turns a valid merchant listing into an error on every out-of-stock page.

⚠️ If you also send a product feed to Google Merchant Center, the price and availability in the markup must match the feed and the page. Mismatches cause "price mismatch" disapprovals in Merchant Center as well as Search Console warnings.

Variants, price ranges and several sellers

Sizes and colours

A product sold in several sizes or colours can be marked up as a ProductGroup with one Product per variant in hasVariant, and variesBy naming what changes, such as https://schema.org/size. Each variant has its own sku, offers and, where it differs, image. Give the group a productGroupID.

{
  "@type": "ProductGroup",
  "name": "Merino crew jumper",
  "productGroupID": "MCJ",
  "variesBy": ["https://schema.org/size", "https://schema.org/color"],
  "hasVariant": [
    { "@type": "Product", "sku": "MCJ-S-NAVY", "size": "S", "color": "Navy",
      "offers": { "@type": "Offer", "price": "65.00", "priceCurrency": "GBP", "availability": "https://schema.org/InStock" } },
    { "@type": "Product", "sku": "MCJ-M-NAVY", "size": "M", "color": "Navy",
      "offers": { "@type": "Offer", "price": "65.00", "priceCurrency": "GBP", "availability": "https://schema.org/OutOfStock" } }
  ]
}

Price ranges and marketplaces

When several sellers offer the same product, or the page lists a range without choosing a variant, use an AggregateOffer with lowPrice, highPrice, priceCurrency and offerCount. AggregateOffer works for product snippets; merchant listings need a single Offer the reader can buy on your page.

Fixing JSON that cannot be read

An unparsable block drops the whole Product, and every other type in the same script. Product names and descriptions are full of characters that break JSON when a template joins strings instead of encoding them.

⚠️ Fix the template, not the page. Build the Product as an object and output it with json_encode() in PHP, | json in Shopify Liquid, or JSON.stringify() in JavaScript. All three escape quotes, line breaks and HTML for you.

The fix, step by step

Step 1

Find every Product block on the page

Open the page source (Ctrl+U, or Cmd+Option+U on a Mac) and search for "Product". On Shopify and WooCommerce there are often three: one from the platform or theme, one from a review app and one from an SEO plugin. The Schema Debugger lists every block with its source position, so you can see which one is the most complete and which ones to switch off.

Step 2

Fix JSON that cannot be read

Paste each block into the Schema Markup Validator. A parsing error gives the line and column; the fault is usually an inch mark, a quote or a raw line break in a product name or description pulled from the catalogue. Fix the template so it outputs the block with a JSON encoder, rather than editing one page, then paste the block in again until the validator reads it cleanly.

Step 3

Add the required fields

Make sure every Product has name, image and offers, with the image as a full https address that loads without a login. Remove Product markup from pages that do not sell a product, such as category pages and blog posts, where it produces "Either offers, review, or aggregateRating should be specified" errors. Give each product page one complete Product block. Re-test each template after the change.

Step 4

Correct price, currency and availability

Check that the price is digits and a full stop only, with no currency sign or thousands separator, that priceCurrency is a three-letter code such as GBP, and that availability is a schema.org URL such as https://schema.org/InStock. Compare each one with the price and stock status the page shows, and with your Merchant Center feed if you send one. Fix any mismatch at the source.

Step 5

Add shipping, returns and identifiers

Add shippingDetails and hasMerchantReturnPolicy to the offer, or a site-wide return policy on your Organization, matching what your delivery and returns pages say. Add brand as a Brand object, and sku, gtin or mpn where the product has them, so Google can match your product to the same item sold elsewhere. Use real barcodes only: an invented or mistyped GTIN does more harm than leaving the field out, and Merchant Center can reject the product for it.

Step 6

Test and request a recrawl

Test the live URL in the Rich Results Test and open both Product snippets and Merchant listings, since an error can appear under one and not the other. When both are clean, use URL Inspection in Search Console and choose Request indexing, then press Validate fix on the Product snippets and Merchant listings reports so Google rechecks every corrected page. Re-test after app updates.

Stopping it from coming back

Questions and answers

1. What is the difference between product snippets and merchant listings?

Product snippets add price, availability and stars to an ordinary search result, and they work on any page about a product, including reviews and comparisons. Merchant listings are the shopping-style results with image, price, delivery and returns, shown in the Shopping tab, image search and product panels, and they only apply to pages where the reader can buy the product from you. Each has its own required fields and its own Search Console report.

2. Why does my product pass one test and fail the other?

Merchant listings need more than product snippets. Product snippets only need a name plus one of offers, review or aggregateRating. Merchant listings need a name, an image, and an offer with a price and a currency code. So a page with only a name and a rating passes product snippets and fails merchant listings. In the Rich Results Test, open both items to see which fields each one is missing.

3. How should I write the price?

Write the price as digits with a full stop for decimals and nothing else: no currency sign, no thousands separator and no words. For example, write 1299.00, not £1,299.00 or from 1299. Put the currency separately in priceCurrency as a three-letter ISO 4217 code such as GBP, EUR or USD. The price must be the one the buyer pays now, match the price on the page, and include VAT for UK shoppers.

4. Do I have to add shipping and returns?

They are recommended rather than required. Without them the page can still get a merchant listing, but Search Console shows missing field warnings and the listing carries less information for shoppers. Add shippingDetails and hasMerchantReturnPolicy to each offer, or set a return policy once on your Organization and shipping at account level in Merchant Center. Whichever you choose, the details must match what the page and your checkout actually say.

5. What should I do when a product is out of stock?

Keep the Product and its offer, and change availability to https://schema.org/OutOfStock. Removing the offer altogether turns a valid merchant listing into a missing offers error on every out-of-stock page, and the page loses its product result entirely. When stock returns, set availability back to InStock. For items that can be ordered before they arrive, use PreOrder or BackOrder instead, so shoppers see the right status in search. Keep the price in the offer while it is out of stock.

6. Can a category page have Product markup?

No. A category page lists many products and is not itself a product, so Product markup does not belong there. Mark up each product on its own product page instead. Product markup on category pages, often added by a theme that outputs a bare Product on every page, is one of the most common sources of Either offers, review, or aggregateRating should be specified errors in the Product snippets report.

7. How long before Search Console shows the fix?

After you request indexing with URL Inspection, Google usually recrawls the page within a few days. The Product snippets and Merchant listings reports update as each affected page is recrawled, which can take up to two weeks across a whole shop. Press Validate fix on each report once the pages are corrected so Google rechecks them as a group, and test each page straight away in the Rich Results Test rather than waiting.

Check your product markup now

The Schema Debugger lists every Product block on a page, checks price, currency and availability formats, flags missing shipping and returns, and compares the markup with the price and stock shown on the page.

Open Schema Debugger Fixes →
💬 Got a problem?
Related Guides: All Schema Debugger Fixes  ·  Schema Debugger Guide  ·  aggregateRating Errors  ·  FAQPage Schema Errors  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.