Commit 768c4f607cf for woocommerce
commit 768c4f607cf750e357f3ed1e4c81561ea7832daf
Author: Vladimir Reznichenko <kalessil@gmail.com>
Date: Wed Sep 30 10:57:39 2026 +0200
Product status counters: fixed a fatal error during background revison save in the admin when a null value was dispatched instead of a post object. (#69231)
Relax \Automattic\WooCommerce\Caches\ProductCountCacheService::update_on_product_status_changed post parameter type expectation to account for prod environments.
diff --git a/plugins/woocommerce/changelog/fix-WOOPLUG-7836-product-counters-fatal-during-background-save b/plugins/woocommerce/changelog/fix-WOOPLUG-7836-product-counters-fatal-during-background-save
new file mode 100644
index 00000000000..9b516c3db84
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-WOOPLUG-7836-product-counters-fatal-during-background-save
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fixed a fatal error in persistent product status counters during background revision save in the admin when a null value was dispatched instead of a post object.
diff --git a/plugins/woocommerce/src/Caches/ProductCountCacheService.php b/plugins/woocommerce/src/Caches/ProductCountCacheService.php
index 76ab5927483..3a0f8a9d468 100644
--- a/plugins/woocommerce/src/Caches/ProductCountCacheService.php
+++ b/plugins/woocommerce/src/Caches/ProductCountCacheService.php
@@ -65,7 +65,7 @@ class ProductCountCacheService {
// transition_post_status owns all mid-lifecycle status changes; woocommerce_new_product corrects for creation-time
// ephemeral transitions before the final status is committed; before_delete_post closes the lifecycle.
- add_action( 'transition_post_status', array( $this, 'update_on_product_status_changed' ), 10, 3 );
+ add_action( 'transition_post_status', array( $this, 'update_on_product_status_changed_skip_autosave' ), 10, 3 );
add_action( 'before_delete_post', array( $this, 'update_on_product_deleted' ), 10, 2 );
}
@@ -130,6 +130,21 @@ class ProductCountCacheService {
// - However, this scenario is a false positive because it occurs within the same PHP process, where hooks are executed sequentially.
}
+ /**
+ * Update the cache whenever a product status changes.
+ *
+ * @param string $new_status The new post status.
+ * @param string $old_status The previous post status.
+ * @param null|WP_Post $post The post object (may be null during autosaves/revisions).
+ *
+ * @return void
+ */
+ public function update_on_product_status_changed_skip_autosave( string $new_status, string $old_status, $post ): void {
+ if ( $post instanceof WP_Post ) {
+ $this->update_on_product_status_changed( $new_status, $old_status, $post );
+ }
+ }
+
/**
* Update the cache whenever a product status changes.
*