Restricted GTIN prefixes in WooCommerce

Some wrong GTINs pass every checksum. Codes on GS1's restricted prefixes — the ranges reserved for in-store items and coupons — are internally valid and publicly meaningless, and Google and the marketplaces reject them like any other bad code. CatalogLift finds each one and names the range it sits on.

Sales-readiness High severity Fix in CatalogLift

Why a restricted prefix is a rejection waiting to happen

GS1 doesn't hand out every number. The ranges beginning 02, 04 and 2xx are reserved for restricted distribution — codes a store or a POS system mints for its own shelves, weighed goods and bundles — and 05, 98 and 99 belong to coupons. Inside the store that printed them they work fine. Submitted to the outside world as a product's GTIN, they are a claim that cannot be true.

  • Google disapproves the offer. Merchant Center checks the prefix along with length and checksum, and a restricted range fails that check outright: the offer is disapproved, the same penalty a mangled code gets — not the softer limited performance of a missing one.
  • Marketplaces won't take the listing. Any channel that checks a barcode against the GS1 database before it accepts an offer treats a restricted range as invalid, and blocks creating or editing the listing entirely.
  • Comparison engines match it to nothing. They identify products by brand, manufacturer code and barcode. An in-store code exists in no public registry, so the product never joins its comparison page.
  • Every ordinary validator waves it through. The checksum is correct, the length is correct, the digits are digits. A spreadsheet check or a quick script sees a healthy code, which is why these survive audits that catch every typo.

How to find restricted prefixes manually

The code is stored in postmeta under _global_unique_id. Restricted ranges depend on the code's length — 2 opens the restricted block in a 13-digit EAN, while 02, 04 and the coupon range 05 lead 12-digit UPCs — so the query checks prefix and length together:

wp db query "SELECT p.ID, p.post_title, m.meta_value AS gtin
FROM wp_posts p
JOIN wp_postmeta m
  ON m.post_id = p.ID AND m.meta_key = '_global_unique_id'
WHERE p.post_type IN ('product', 'product_variation')
  AND p.post_status = 'publish'
  AND m.meta_value REGEXP '^[0-9]+$'
  AND (
       (LENGTH(m.meta_value) = 13 AND LEFT(m.meta_value, 1) = '2')
    OR (LENGTH(m.meta_value) = 13 AND LEFT(m.meta_value, 2) IN ('98', '99'))
    OR (LENGTH(m.meta_value) = 12 AND LEFT(m.meta_value, 2) IN ('02', '04', '05'))
  )
ORDER BY p.ID;"

Adjust the table prefix if yours isn't wp_. Codes with the wrong length or characters are a different problem — the invalid GTIN check — and are deliberately left out here.

The query finds the codes; it cannot find the replacements. Each hit means tracing where the code came from — a POS export, a labelling tool, a supplier sheet — and then obtaining the product's real GTIN, which is legwork no SQL shortens. And the next export from the same system will plant the same codes again.

How CatalogLift finds it

Every scan validates each product's identifier field deterministically, and a code that passes length and checksum is then checked against the GS1 restricted and coupon ranges for its length. A code on one of those ranges becomes a finding at high severity, and the evidence names the range and what it is reserved for — an in-store restricted-distribution code, a coupon code — so you can tell at a glance which system most likely minted it.

There is no AI on this path, and the fix is deliberately not an edit: changing a digit until the prefix looks public would forge a claim on a code you don't own. The real fix is the real code — from the supplier or the packaging for resold products, from your own GS1 license for goods you brand yourself — typed into CatalogLift and decided by you. Only then does CatalogLift write it to WooCommerce, and the next scan verifies the new code is valid and public. Because CatalogLift keeps watching, the next POS export that reintroduces in-store codes surfaces immediately, and the first scan costs nothing and needs no card.

Fair questions, straight answers.

The checksum is correct — how can the code be wrong?

The checksum only proves the digits are internally consistent. GS1 reserves whole prefix ranges for codes that never leave the building: restricted-distribution ranges like 02, 04 and the 2xx block, used for store-internal items and variable-weight goods, and the coupon ranges 05, 98 and 99. A code minted there validates perfectly and identifies nothing outside the store that printed it.

How do these codes end up in a catalog?

Usually honestly. A POS system or a label printer generates in-store codes for deli counters, bundles or private-label items, and a later export carries them into WooCommerce as if they were EANs. Some barcode tools also hand out 2xx codes as free placeholders. Nobody typed a wrong digit — the code was never a public GTIN to begin with.

Can I just change a digit so the prefix looks normal?

No — that turns an internal code into a claim on a code you don't own, most likely someone else's product. The only fix is the real GTIN: from your supplier's data or the manufacturer's packaging for resold goods, or from your own GS1 license for products you brand yourself. CatalogLift never edits a code into validity.

Who actually rejects these codes?

Google Merchant Center disapproves offers whose GTIN sits on a restricted prefix, exactly as it does for a failed checksum. Marketplaces that validate barcodes against the GS1 database treat a restricted range as invalid and block the offer at creation. Comparison engines match products by barcode, and an in-store code matches nothing.

See it on your own catalog.

Create a free account, connect your WooCommerce store and this check runs on your real products, along with the rest.

No card required. The free plan never writes to WooCommerce.