Duplicate SKUs After a Bulk Import on a Multi-Source Inventory
A merchant uses Adobe Commerce 2.4.6 with Multi-Source Inventory (MSI) enabled and three sources. They run a nightly bin/magento import (the catalog_product_import job) of ~40,000 rows keyed by SKU. Symptoms: (1) a subset of products appear twice in the storefront grid but have identical SKUs in the admin; (2) catalog_product_entity has one row per SKU, yet the duplicate appears in the price index; (3) some rows update price but not description, which is scoped to the default store view. The import file's _attribute_set and _store columns are inconsistent: some rows use default, others are blank. The developer suspects the indexer and reindexes manually, which does not fix the duplicates.
1. Explain why identical SKUs can appear duplicated in the storefront while `catalog_product_entity` shows a single row. Where should the developer look?
Read the worked answer
catalog_product_entity holds one row per SKU (the SKU is the business key), so a true duplicate there is impossible without a constraint violation. The duplication therefore lives in a downstream table or index, not the entity table. Prime suspects:
- Store-scoped tables -
catalog_product_entity_varchar/int/textrows keyed by(entity_id, attribute_id, store_id). If the import wrote the same attribute under two differentstore_idvalues (e.g.,0and1), the storefront (which merges default + store view) can render conflicting values, and some grid queries join without deduplicating. - Index tables -
catalog_product_index_price,catalog_category_product_index, and the MSIinventory_*/salable-quantity index. A stale or double-inserted index row (e.g., from a partial import that ran while an indexer was inUpdate by Schedulemode) surfaces as a duplicate grid entry even though the source entity is unique. - MSI source items -
inventory_source_itemis keyed by(source_code, sku); a malformed import can leave orphaned source rows for a SKU that was later reassigned.
The developer should query these tables by SKU/entity_id and compare store_id values rather than reindexing blindly - reindexing rebuilds from source data, so if the source data is inconsistent, the duplicate returns.
2. The `_store` column is inconsistent (`default` vs blank). What is the correct behavior for a scoped attribute like `description`, and how should the import be corrected?
Read the worked answer
In Commerce, description is typically scoped to store view (global vs store scope is defined in eav_attribute.scope). A blank _store in the import CSV means the default scope (store_id 0 / admin), while default is a store code that resolves to a specific store view. Mixing them writes the same attribute to different store_id rows.
Correct approach:
- Decide the intended scope. If the value should apply everywhere, write it once at the default/admin scope (blank
_store) and leave store-view rows empty so they inherit. - If it should differ per store view, use the exact store code in
_storefor every row and never mix blank anddefaultfor the same attribute. - Normalize the file before import: one consistent
_storevalue per logical batch, and verify the attribute'sscopeineav_attributeso you know whether the value is even storable per store.
After correcting the source data, run bin/magento indexer:reindex for the affected indexers (or let the schedule run) so the index tables are rebuilt from the now-consistent EAV rows. Reindexing before fixing the source data is why the earlier attempt failed.