#66733·woocommerce

WC_Order_Item_Product::set_product() leaves stale variation attribute meta on the item

Author: oaratovskyiCreated Jul 16, 2026Updated Sep 18, 2026
LabelsBugProduct/Inventory ManagementRubikneeds: engineering

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:

php
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

php
$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 variation

Why 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.phpset_variation() and set_product().