Commit 6c88ed4ff1d for woocommerce
commit 6c88ed4ff1d638309c2ba747db52987d50d29184
Author: Luis Felipe Zaguini <26530524+zaguiini@users.noreply.github.com>
Date: Fri Nov 6 13:07:43 2026 -0300
Save nothing when an ability's change() returns an empty WP_Error (#70752)
Save nothing when change() returns an empty WP_Error
execute_change() marked an empty WP_Error from change() as a refusal,
but decided the save from has_errors(), so the change was saved. Use
the same check for both, as the change() docblock describes.
Also name order writes in the changelog entry of #70709.
diff --git a/plugins/woocommerce/changelog/wooai-core-refusals-in-one-message b/plugins/woocommerce/changelog/wooai-core-refusals-in-one-message
index 67738cf2d2f..39345903d4a 100644
--- a/plugins/woocommerce/changelog/wooai-core-refusals-in-one-message
+++ b/plugins/woocommerce/changelog/wooai-core-refusals-in-one-message
@@ -1,4 +1,4 @@
Significance: patch
Type: tweak
-Ability errors now list every refused value of a product write in one message, so AI agents can fix them all in one retry.
+Ability errors now list every refused value of a product or order write in one message, so AI agents can fix them all in one retry.
diff --git a/plugins/woocommerce/src/Abilities/ActionableAbility.php b/plugins/woocommerce/src/Abilities/ActionableAbility.php
index 0049fd33518..932dd33c2cc 100644
--- a/plugins/woocommerce/src/Abilities/ActionableAbility.php
+++ b/plugins/woocommerce/src/Abilities/ActionableAbility.php
@@ -280,7 +280,7 @@ abstract class ActionableAbility extends Ability {
$refused
);
}
- if ( $refused->has_errors() ) {
+ if ( $core_refused || $refused->has_errors() ) {
return self::join_refusals( $refused );
}
diff --git a/plugins/woocommerce/tests/php/src/Internal/AbilitiesApi/ExtensibleAbilitiesTest.php b/plugins/woocommerce/tests/php/src/Internal/AbilitiesApi/ExtensibleAbilitiesTest.php
index d07ae5cc553..85974bbd840 100644
--- a/plugins/woocommerce/tests/php/src/Internal/AbilitiesApi/ExtensibleAbilitiesTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/AbilitiesApi/ExtensibleAbilitiesTest.php
@@ -3557,6 +3557,45 @@ class ExtensibleAbilitiesTest extends \WC_REST_Unit_Test_Case {
$this->assertSame( 'Pen', wc_get_product( $product->get_id() )->get_name() );
}
+ /**
+ * @testdox Should save nothing when change() returns a WP_Error without errors.
+ */
+ public function test_an_empty_error_from_change_saves_nothing(): void {
+ $product = \WC_Helper_Product::create_simple_product( true, array( 'name' => 'Pen' ) );
+ $ability = new class(
+ 'test-ext/product-rename',
+ array(
+ 'label' => 'Rename product',
+ 'description' => 'Renames a product.',
+ 'category' => 'woocommerce',
+ 'output_schema' => array( 'type' => 'object' ),
+ 'permission_callback' => '__return_true',
+ )
+ ) extends TestProductRenameAbility {
+ /**
+ * Rename the product, then refuse with an empty error.
+ *
+ * @param \WC_Product $subject Product.
+ * @param array $input Ability input.
+ * @return \WP_Error
+ */
+ protected function change( $subject, array $input ) {
+ parent::change( $subject, $input );
+ return new \WP_Error();
+ }
+ };
+
+ $result = $ability->execute_change(
+ array(
+ 'id' => $product->get_id(),
+ 'name' => 'Pencil',
+ )
+ );
+
+ $this->assertWPError( $result );
+ $this->assertSame( 'Pen', wc_get_product( $product->get_id() )->get_name() );
+ }
+
/**
* @testdox Should return a fixed 500 error when the order write fails, though the order's save() catches the exception.
*/