WC_Order_Item_Product::set_product() leaves stale variation attribute meta on the item
Found while working on woocommerce/woocommerce#52292, which fixed a different half of the same code path.
Describe the bug
WC_Order_Item_Product::set_variation() stores each variation attribute as item meta, stripping the attribute_ prefix:
public function set_variation( $data = array() ) {
if ( is_array( $data ) ) {
foreach ( $data as $key => $value ) {
$this->add_meta_data( str_replace( 'attribute_', '', $key ), $value, true );
}
}
}So a variation contributes meta like color => Green. When set_product() later switches the item to a different product, that meta stays behind — a simple-product line item can still render "Color: Green" in admin, in emails, and on the customer-facing order.
This is broader than a variation → simple switch. Because add_meta_data( ..., true ) is unique-per-key, switching variation → variation only overwrites shared attribute keys. Attributes present on the old variation but absent from the new one linger too.
Expected behavior
Switching a line item's product should not leave attribute meta from the previous variation on the item.
Actual behavior
The previous variation's attribute meta remains on the item indefinitely.
Steps to reproduce
$item = new WC_Order_Item_Product();
$item->set_product( $variation_with_color_green );
$item->set_product( $some_simple_product );
var_dump( $item->get_variation_id() ); // 0 (correct, as of #52292)
var_dump( $item->get_meta( 'color' ) ); // 'Green' — stale, belongs to the old variationWhy this wasn't fixed in woocommerce/woocommerce#52292
It isn't safe to clean up naively, which is why it's split out rather than bundled.
Because the attribute_ prefix is stripped on write, the stored meta key color is indistinguishable from a merchant's own custom color meta. Anything that clears it risks deleting real data.
A targeted cleanup — load the old variation and delete only meta keys matching its attribute names — is conceivable, but has a hole: if the old variation has since been deleted, wc_get_product() returns false and there is nothing left to diff against.
Worth noting woocommerce/woocommerce#52292 did not make the display any worse: set_name() already ran in both branches of set_product(), so a switched item already showed the new product's name beside the stale attribute meta.
Additional context
Relevant code: plugins/woocommerce/includes/class-wc-order-item-product.php — set_variation() and set_product().
Source: woocommerce/woocommerce