Duplicate SKUs and why sync goes wrong
Every multi-store sync tool matches products by SKU, so your SKU hygiene sets a ceiling on how well any of them can work. How to find the problems first.
Every tool that syncs inventory between two Shopify stores has to answer one question before it can do anything useful: which item over here is the same as which item over there? The answer is almost always “the ones with the same SKU” — and that means your SKU hygiene sets a hard ceiling on how well any of these tools can work, regardless of which you choose.
Why SKU is the join key
It is the last field standing.
- Product and variant IDs are generated per store. The same physical item has entirely different IDs in each one, and there is no lookup between them.
- Titles get edited, localised and A/B tested. “Wool Beanie” in one store is “Merino Beanie (Winter)” in the other within a season.
- Handles are derived from titles and inherit the same problem.
- Barcodes would be ideal — they are globally unique by design — but most catalogues have them on a minority of items, if any.
That leaves the SKU: a merchant-controlled string that usually does mean the same thing in both stores. It is the join key by elimination, not because anyone designed it to be one. And Shopify enforces nothing about it — not uniqueness, not presence, not format.
The three ways it goes wrong
1. Blank SKUs — invisible
A variant with no SKU cannot be matched to anything. It is simply not in the sync. This is the mildest failure: nothing is corrupted, the item just never moves. It is worth knowing about because the symptom — “this product never syncs” — looks identical to a bug, and people spend a lot of time chasing it.
2. SKUs in one store only — expected
Store A has 900 SKUs, store B has 700, and 650 overlap. That is normal. The stores are not meant to be identical. What matters is that the tool tells you which ones did not match, so you can confirm the list is the one you expected rather than a symptom of something else.
The dangerous variant of this: a tool that responds to an unmatched SKU by creating a new product in the other store. That is how catalogues get duplicated, and it is one of the most common complaints in public reviews across this whole app category. Creating products is a reasonable feature when you asked for it and a catastrophe when you did not.
3. Duplicate SKUs within one store — the damaging one
This is the case with no good outcome. Suppose store A has SHIRT-M on two different variants — perhaps a 2024 version and a 2025 version, or the same shirt listed under two products. Store B has one variant with SHIRT-M.
Which quantity should store B follow? There is no correct answer. The tool can:
- Guess — pick the first, or the newest, or the one with more stock. Wrong about half the time, and wrong silently, which is the worst property a sync can have.
- Sum them — plausible-sounding and usually wrong, because the two variants are typically different things that happen to share a string.
- Refuse and report — skip the SKU, tell you it is ambiguous, and let you fix the data. Nothing syncs for that item, but nothing is corrupted either.
The third is the only defensible behaviour, and it is worth confirming which one your tool does before you rely on it.
A duplicate SKU produces oversells that look like race conditions — wrong quantity, no obvious cause — but repeat forever and are not fixed by faster syncing. If one specific item keeps going wrong while everything else behaves, check for a duplicate before you blame timing.
How duplicates get there
Nobody creates them deliberately. The common routes:
- Duplicating a product in the admin to make a variant of it. Shopify copies the SKUs along with everything else.
- Seasonal relists — last year’s item is kept for its reviews and history, and the new one reuses the SKU.
- CSV imports that add rather than update, usually because the handle changed between exports.
- Placeholder SKUs —
TBD,NEW,-, or1— applied to several items during a bulk edit and never revisited. - Bundles and kits given the SKU of their main component.
- Merged catalogues after an acquisition or a store rebuild, where two numbering schemes collided.
Finding them, in about twenty minutes
- In the Shopify admin, go to Products and choose Export. Export all products as a CSV for Excel, Numbers or Sheets.
- Open the file and find the Variant SKU column. Every row is a variant, so a product with five sizes is five rows.
- In a new column, flag duplicates. In Sheets or Excel, if the SKUs are in column
T:=IF(T2="", "BLANK", IF(COUNTIF(T:T, T2)>1, "DUPLICATE", "")) - Filter that column for
DUPLICATEandBLANK. Note the counts. - Repeat for the second store, then compare the two SKU lists to see the overlap. A
MATCHorCOUNTIFacross the two sheets gives you the answer that any sync tool is going to arrive at as well.
What you now know, before installing anything: how many items can sync at all, how many are ambiguous, and whether the non-overlap is the set you expected. That is most of the risk, surfaced for the cost of an afternoon.
Fixing what you find
Duplicates. Decide which variant owns the SKU and give the other one a new one. Prefer changing the one that is discontinued, lower-volume, or not sold in the second store. Be aware that SKUs may appear in your 3PL, accounting and label systems too — changing one is a small cross-system migration, not just a field edit.
First, though, check that it is a duplicate. A SKU on two variants that are genuinely the same physical item is not a data error — it is an accurate description of one shelf sold two ways, and renaming one of them makes the data worse. That case wants one shared count across both rather than two SKUs. The error is two different items wearing one code; the not-error is one item wearing it twice.
Blanks. Fill them, using a scheme you can extend. If an item genuinely should not sync, leaving it blank is a legitimate way to exclude it — just do it knowingly rather than by omission.
Near-misses. The ones that cost the most time: SHIRT-M against shirt-m, trailing spaces, SHIRT_M against SHIRT-M. Tools differ on case and whitespace handling, and a rule you cannot see is a rule you cannot rely on. Normalise your own data rather than depending on the tool’s opinion of it.
What to demand from any sync tool
- A match report you can read before anything is written — matched, unmatched, ambiguous, listed by SKU.
- Explicit refusal on duplicates, not a silent pick.
- No product creation unless you asked for it in as many words.
- A re-runnable match, because catalogues change and matching quietly breaks after edits.
- A write log, so a wrong number can be traced to whatever wrote it.
Where StockUnison sits
StockUnison matches on SKU and treats ambiguity as a stop condition: a SKU that appears more than once in either store is reported and never synced, in preview mode and in live mode alike. Unmatched SKUs are listed rather than guessed at, and no product is ever created in a connected store. Variants with no SKU are named in their own banner, because they appear nowhere else in a report organised by SKU. The match report is re-runnable and readable before anything is written, which is the whole reason every connection starts in preview mode.
On the near-miss question above, its rule is stated rather than hidden: case and surrounding whitespace are ignored, so abc-1 and ABC-1 are the same SKU. Note what that implies — two variants differing only in case are a duplicate, and will be reported as one rather than silently treated as two different items.
The match report and its duplicate list are on the free plan, which is the point: the audit above can be run with the app rather than a spreadsheet, without paying.
None of that fixes your SKUs. It tells you which ones need fixing, and refuses to act on the ones it cannot be sure about — which is the most a tool can honestly promise here.
Two stores. One truth.
StockUnison mirrors one store's stock into every other store you connect — matched by SKU, previewed in dry run, and logged write by write.
Install on ShopifyFree plan you can stay on · live syncing from $19/mo