Why a missing price stops the sale
WooCommerce does not refuse to publish a product with an empty price field. It publishes it, indexes it, and shows it in category pages and search results as if it were for sale. The difference only appears at the moment a shopper wants to buy.
- The add-to-cart button disappears. WooCommerce marks the product as not purchasable, so the product page shows a description and images with no price and no way to order.
- Shopping feeds reject the item. Google Merchant Center and marketplace feeds require a price on every offer; an unpriced product is disapproved and takes its ad spend with it.
- Search traffic arrives and bounces. The page still ranks and still earns clicks, and each one ends in a dead end that reads as a broken store rather than a temporary gap.
- Zero is different from missing. A free offer has an explicit price of 0. Keep it when that is your intention; it does not need a positive price merely to clear this check.
How to find products without a price manually
The regular price lives in postmeta under _regular_price.
With WP-CLI, this query lists published simple products whose regular
price is missing, empty, or negative:
wp db query "SELECT p.ID, p.post_title, m.meta_value AS regular_price
FROM wp_posts p
JOIN wp_term_relationships tr ON tr.object_id = p.ID
JOIN wp_term_taxonomy tt
ON tt.term_taxonomy_id = tr.term_taxonomy_id AND tt.taxonomy = 'product_type'
JOIN wp_terms t ON t.term_id = tt.term_id AND t.slug = 'simple'
LEFT JOIN wp_postmeta m
ON m.post_id = p.ID AND m.meta_key = '_regular_price'
WHERE p.post_type = 'product' AND p.post_status = 'publish'
AND (m.meta_value IS NULL OR m.meta_value = ''
OR CAST(m.meta_value AS DECIMAL(18,4)) < 0)
ORDER BY p.ID;"
Adjust the wp_ prefix if yours differs. The
product_type join keeps variable parents out of the list,
since their prices live on the variations.
The query gives you a list, and that is where it stops. Each row still has to be opened in wp-admin, priced, and saved by hand, one product at a time. Values that are not numbers, such as a price typed with a currency symbol or a stray letter, may survive the CAST or fail it depending on your database mode. And the query answers once: the next CSV import or a plugin that clears a field can leave a product unpriced again without anyone noticing until an order fails to happen.
How CatalogLift finds it
Every scan reads the regular price of each current, published simple product in the synced catalog and asks one question: is there a price a shopper could actually pay? An empty value, a negative number and a value that does not parse as a decimal all fail that question in the same way, and each one becomes a finding at critical severity with the product, the field, and the value it found as evidence. Sale prices are a separate check; a product with no sale price is not a problem here.
Because a price is a decision only you can make, this check has no AI draft. You type the regular price directly inside CatalogLift, see it as a before/after change against the product, and approve it. Only then is it written to WooCommerce, and the next scan verifies the product is sellable again. CatalogLift keeps watching, so a future import that blanks the price becomes a fresh finding rather than a quiet gap in your revenue.