"Invalid or duplicated SKU": two products share one SKU
Duplicate SKUs break stock sync, exports and feeds — and WooCommerce only warns you at the moment you save.
The symptom
You try to save a product and WooCommerce refuses: “Invalid or duplicated SKU.” Or worse — nothing refuses, because the duplicate was created by an import, a bulk edit or a plugin that never went through the product screen. The catalogue looks fine. The stock numbers do not.
What it costs
An SKU is the identifier every system outside WooCommerce uses to mean this exact product. When two products carry the same one:
- Stock sync goes to the wrong product. An ERP, a warehouse app or a marketplace integration updates one row and the customer buys the other.
- Product feeds collapse. Google Merchant Center and most marketplaces treat SKU (or GTIN plus SKU) as the key; duplicates get merged or rejected, and you lose the listing without being told.
- Reporting lies. Any report that groups by SKU adds two products together.
None of this shows up as an error on the storefront, which is why duplicates survive for years.
How to check it yourself
From WP-CLI, without any plugin:
wp db query "SELECT meta_value AS sku, COUNT(*) AS n \
FROM wp_postmeta pm JOIN wp_posts p ON p.ID = pm.post_id \
WHERE pm.meta_key = '_sku' AND pm.meta_value <> '' \
AND p.post_status IN ('publish','draft','private') \
GROUP BY meta_value HAVING n > 1"
Replace wp_ with your table prefix. On a store with product variations the same query needs
post_type IN ('product','product_variation') — variations carry their own _sku, and a variation
duplicating its parent is the most common case of all.
How to fix it
- Decide which product keeps the SKU — usually the one with order history.
- Give the other a new SKU that follows your own scheme. Do not leave it blank if anything downstream consumes it.
- If the duplicate is a variation of the same parent, check whether it is a leftover from a deleted attribute value; those are usually safe to remove rather than renumber.
- Fix the import that created it, if that is what created it — otherwise the duplicate comes back with the next run.
When it is a false positive
- You deliberately reuse SKUs across languages. WPML and Polylang translations of one product legitimately share an SKU. Merchlint excludes known translation pairs; if you use a different multilingual setup, hide the finding.
- Draft duplicates you are about to publish as a replacement for the original.
- Bundles that inherit a component SKU by design, in some bundle plugins.
How Merchlint finds it
Merchlint reads _sku from published products and from their variations, groups by exact value,
ignores empty values, and reports each product that shares its code with another product — or with
a variation of a different product. The finding shows the SKU, the product IDs sharing it and each
product’s status, so you can decide which one keeps it without opening both.
Two cases stay out, and both on purpose. A product sharing a code with its own variation is not reported: WooCommerce shows the parent’s SKU on a variation that has none, so that pair is how the platform behaves rather than a fault. And two variations of one product sharing a code while the product itself has none — a finding belongs to a product, and there is nothing to attach it to there. Variations sitting in the trash never count.
Confidence: F — fact from the database. No interpretation, no threshold. Two rows have the same value or they do not.
This check runs in the free version, on every product, with no cap. If SKUs and GTINs are all you need, the same engine also comes as a separate free plugin with just these checks: Duplicate SKU and GTIN Finder.
Disagrees with what you see in your store? Write to us — we read every message. contact@merchlint.com