Why a missing SKU holds products back
The SKU is the one identifier that follows a product out of WooCommerce and into everything else you run: order exports, stock syncs, accounting, shipping labels, product feeds. WooCommerce does not force you to fill it in, so a product can be live for months while every system that needs the code silently works around it.
- Orders lose their identity. Line items in an order carry the SKU into packing slips and exports. When it is empty, the warehouse gets a name and a price, and the accounting file gets a blank cell that someone has to resolve by hand.
- Feeds and integrations reject the product. Google Merchant Center, marketplace connectors and most ERP or inventory tools use the SKU as the product key. Without one the product is dropped from the feed, or worse, matched to the wrong record.
- Reports can't be trusted. Stock and sales reports grouped by SKU lump every blank product into one anonymous row, so the numbers for the products that do have a code are the only ones you can act on.
- Duplicates creep in unnoticed. WooCommerce enforces SKU uniqueness only for products that have one. A gap today becomes a collision tomorrow when someone fills it in without checking the rest of the catalog.
How to find missing SKUs manually
The SKU is stored in postmeta under _sku. WooCommerce
usually writes the row even when the field is empty, but not always,
so the query has to catch both a missing row and an empty value. With
WP-CLI, this lists products without a SKU:
wp db query "SELECT p.ID, p.post_title, p.post_status
FROM wp_posts p
LEFT JOIN wp_postmeta m
ON m.post_id = p.ID AND m.meta_key = '_sku'
WHERE p.post_type = 'product'
AND p.post_status IN ('publish', 'draft', 'pending', 'private')
AND (m.meta_value IS NULL OR m.meta_value = '')
ORDER BY p.post_status, p.ID;"
Adjust the table prefix if yours isn't wp_. Change
post_type to product_variation to run the
same check on variations.
The list is easy; keeping it empty is not. The query answers for one moment, and the next CSV import, the next product someone duplicates in wp-admin and forgets to rename, or the next connector that creates products without a code puts fresh gaps back on the list. It also hands you IDs, not the product screens, so fixing a long list means opening each one in wp-admin, typing a code, and hoping it doesn't collide with a SKU already in use.
How CatalogLift finds it
Every scan walks the whole synced catalog, published and unpublished products alike, and reads each product's SKU as it came from WooCommerce. A product whose SKU is missing or empty becomes a finding at high severity, with the product and the empty field as evidence, next to everything else the scan surfaced. Variations are checked separately, so a variable product and its variations each get their own finding.
There is no AI on this path, because a SKU is a fact about your business, not something to draft. You type the correct code for each product inside CatalogLift and approve it. Nothing is written before that decision; on it, CatalogLift writes the code to WooCommerce and the next scan verifies the field is filled. Because CatalogLift keeps watching, an import that creates new products without a code shows up as a fresh finding instead of a quiet regression, and the first scan costs nothing and needs no card.