Why a priceless variation costs you sales
For a variable product the price does not live on the product at all — it lives on each variation. The parent only shows a range assembled from its children. When one variation has no regular price, WooCommerce quietly drops that combination from what can be sold, and nothing in wp-admin raises a flag.
- The variation cannot be purchased. WooCommerce marks a variation without a price as not purchasable, so the shopper who picks that size or color meets a missing add-to-cart button or an unavailable notice.
- The product page misleads. The price range in the heading is computed only from priced variations, so a shopper reads one range, chooses the option they came for, and cannot check out.
- Feeds and structured data break. Product feeds and schema markup expect an offer per variation; an empty price yields rejected items or offers with no price at all.
- It hides well. The parent product looks fine in the product list, the Variations tab collapses each row by default, and an import that misses a price column on one row leaves no trace except a shopper who could not buy.
The usual causes are ordinary: a CSV import that mapped the price column for simple products but not for variations, a new size added in a hurry with the price left for later, or a plugin sync that created the variation before the price arrived. None of them produce an error, which is why the gap tends to be discovered by a customer rather than by the team.
How to find variations without a price manually
Variations are rows in wp_posts with the type
product_variation, and their regular price lives in
wp_postmeta under _regular_price. With
WP-CLI, this query lists every published variation of a published
parent whose regular price is empty or missing:
wp db query "SELECT v.ID AS variation_id, p.ID AS product_id, p.post_title,
m.meta_value AS regular_price
FROM wp_posts v
JOIN wp_posts p ON p.ID = v.post_parent
LEFT JOIN wp_postmeta m
ON m.post_id = v.ID AND m.meta_key = '_regular_price'
WHERE v.post_type = 'product_variation' AND v.post_status = 'publish'
AND p.post_type = 'product' AND p.post_status = 'publish'
AND (m.meta_value IS NULL OR m.meta_value = '' OR m.meta_value + 0 <= 0)
ORDER BY p.ID, v.ID;"
Adjust the table prefix if yours isn't wp_. Disabled
variations carry the status private and are left out on
purpose.
This finds the rows, with the usual gaps. It reads meta as text, so a stray space or a comma decimal from an import can slip past the numeric check. It leaves you with a list of IDs and no way to enter the prices except opening each product's Variations tab, expanding the row, saving and moving on. And it answers once: the next CSV import or plugin sync that drops a price column reintroduces the problem without anyone running the query again. On a catalog with many variable products the Variations tab is also the slowest screen in wp-admin to work in, so a list of a few hundred IDs is an afternoon, not a query.
How CatalogLift finds it
Every scan walks the variations of every published variable product in the synced catalog and flags each variation whose regular price is empty or not greater than zero. Sale price is not consulted — a variation with a sale price but no regular price is still flagged, because that is not a state WooCommerce sells from cleanly. The finding names the variation and its parent product and carries the empty regular price as evidence, at critical severity, since the item cannot be bought at all. Draft parents, disabled variations and simple products are outside this check; a simple product without a price is its own finding.
There is no AI on this check — the right price is a fact only you know, and no draft could supply it honestly. Instead the finding offers a field: you type the regular price inside CatalogLift, see it as a before/after diff next to the evidence, and approve. Only then does CatalogLift write it to WooCommerce, and the next scan verifies the variation is priced and purchasable. Findings on the same product sit together, so a product whose whole size run came in unpriced can be worked through in one sitting instead of one variation at a time in wp-admin. Because CatalogLift keeps watching, an import that later blanks a price on a new variation becomes a fresh finding rather than a silent loss of sales. The first scan is free and needs no card, and nothing in your store changes until you approve it.