WooCommerce 10.8 Stock Not Deducting After Order Status

WooCommerce 10.8 manual orders not reducing stock after a status switch to processing? Here is the hook rename and the one-line fix that restores it cleanly.
WooCommerceWordPressHPOS
June 5, 20265 min read912 words

The Problem

I ran into this on a client project two days after the WooCommerce 10.8 release. The shop owner creates manual orders in the admin for phone customers: pick the products, add the customer, set status to Processing, send the invoice. Inventory has been pulling out of stock that way for years.

After the 10.8 update, the workflow looked identical from the admin side. The order saved, the status moved to Processing, the email went out. But the product stock did not move. A SKU with five units that just sold one still showed five units. By the time the team noticed, three orders had oversold the same product.

The order notes contained nothing about stock reduction. The wp_wc_orders table held the order. The wp_wc_orders_meta table had the line items. Nothing in the error log. Just no _reduced_stock meta on the line items and no stock movement on the product itself.

Order #18432: status changed from pending to processing.
Order #18432: customer invoice email sent.
[no stock reduction entry]

The same thing surfaced on a second client running a custom plugin that hooked woocommerce_reduce_order_stock to post a Slack alert when low-stock thresholds tripped. The alert stopped firing entirely, even on orders placed through the storefront. Two symptoms of the same change.

Why It Happens

WooCommerce 10.8 split the order stock reduction pipeline. The legacy woocommerce_reduce_order_stock action used to fire once whenever any order moved to a stock-reducing status. It was deprecated in favour of two narrower actions tied to HPOS-aware order objects:

  • woocommerce_order_status_processing_reduce_stock
  • woocommerce_order_status_completed_reduce_stock

The deprecation is documented in the 10.8 release notes but the legacy action is still registered for backward compatibility. The catch: it only fires on storefront-placed orders, not on orders created through wc_create_order() or the admin New Order screen, because the new admin flow routes through OrdersTableDataStore::reduce_order_stock() which uses the new actions exclusively.

Plugins, themes, or custom mu-plugins hooked to the legacy action keep working for checkout-flow orders but silently no-op for admin-created orders. Worse, any custom code that disabled the legacy action to replace stock handling, common in B2B stores with reserve-stock workflows, now disables nothing on the admin path. The default reduction does not run there either.

The result depends on which side of the split your code lives on. If you added behaviour, you lose it on admin orders. If you removed default behaviour, you lose your replacement and the default still does not run, leaving stock untouched.

The Fix

Two changes, depending on what your code does.

If you are extending the default behaviour, hook both new actions in parallel with the legacy one. They pass the order object as the first argument, the same way the old hook did:

add_action( 'woocommerce_order_status_processing_reduce_stock', 'qc_notify_low_stock', 10, 1 );
add_action( 'woocommerce_order_status_completed_reduce_stock',  'qc_notify_low_stock', 10, 1 );

// Keep the legacy hook for older WooCommerce versions still running on staging.
add_action( 'woocommerce_reduce_order_stock', 'qc_notify_low_stock', 10, 1 );

function qc_notify_low_stock( $order ) {
    foreach ( $order->get_items() as $item ) {
        $product = $item->get_product();
        if ( ! $product || ! $product->managing_stock() ) {
            continue;
        }
        if ( $product->get_stock_quantity() <= $product->get_low_stock_amount() ) {
            qc_send_slack_low_stock_alert( $product );
        }
    }
}

If you are replacing the default reduction with custom logic (reserve-stock workflows, B2B holds), the cleanest fix is to short-circuit the new HPOS path and run your code instead. WooCommerce 10.8 exposes a filter for exactly this:

add_filter( 'woocommerce_can_reduce_order_stock', function ( $can_reduce, $order ) {
    if ( get_post_meta( $order->get_id(), '_b2b_hold', true ) === 'yes' ) {
        qc_reserve_stock_for_b2b( $order );
        return false; // Tell core not to reduce on this order.
    }
    return $can_reduce;
}, 10, 2 );

Return false and core skips both the legacy and the new reduction. Return true (the default) and core proceeds with its normal HPOS-aware reduction, which now correctly fires on admin orders.

Verify the fix before closing the ticket. Create a manual order in the admin for a product with a known stock count, set it to Processing, and check the product stock from WP-CLI:

wp wc product get 18432 --user=1 --field=stock_quantity

The number should drop by the line item quantity. If it does not, you still have a plugin holding the legacy hook closed.

The Lesson

WooCommerce 10.8 finished a migration the HPOS rollout started a year ago: the order pipeline now uses status-specific actions, and the catch-all legacy hook is on its way out. If you wrote stock-handling code against the legacy action, it works on storefront orders and silently fails on admin orders until you hook the new actions too. The filter woocommerce_can_reduce_order_stock is the right place to override defaults across both paths.

If your WooCommerce upgrade broke an integration and the cause is buried in a hook rename, that is a project I get paid to untangle. See my services. For another HPOS regression that hits after an upgrade, read WooCommerce HPOS bulk status update fail.

Stuck on a WooCommerce 10.8 upgrade? Get it shipped.

Back to blogStart a project