A double-applied discount does not announce itself. There is no warning banner and no failed job. The prices simply sit lower than they should, and the first real signal is a margin that looks wrong after the orders land.
Confirm it with arithmetic
A percentage applied twice compounds. Take a variant you know the original price of and check which number it matches.
| Original | 25% off once | 25% off twice |
|---|---|---|
| $49.00 | $36.75 | $27.56 |
| $99.00 | $74.25 | $55.69 |
| $199.00 | $149.25 | $111.94 |
If the storefront shows the third column, the discount ran twice. This also tells you the multiplier you need to undo it: divide by 0.75 twice, not once.
Why it happens
- The same rule was saved twice, so two jobs both matched the same variants.
- A job was retried after a timeout, and the retry re-applied instead of resuming.
- The discount was written against the current price rather than a stored original, so every run compounds on the last one.
The root cause
Reason three is the structural one. If an app computes the sale price from whatever the price happens to be right now, running it twice is guaranteed to compound. Computing it from a snapshot taken before the first change cannot compound, no matter how many times it runs.
Getting the prices back
- If you have a snapshot or a CSV export from before the sale, restore from that. It is the only source that is definitely correct.
- If you do not, reconstruct: divide the current price by (1 - discount) once for every time it ran, then check a handful against old orders or your supplier sheet.
- Fix compare-at prices in the same pass. A compare-at left behind from the doubled run keeps showing a strikethrough that no longer matches anything.
- Only then re-run the sale, once, and verify a sample before walking away.
Preventing the next one
- Preview the resulting price per variant before anything is written. A doubled discount is obvious in a preview and invisible on a storefront.
- Snapshot the original price and compare-at price before the first change, and keep that file yourself.
- Make the sale price derive from the snapshot, not from the live price, so a re-run is idempotent.
SaleGuard does those three things by default: it takes the snapshot first, shows the exact resulting price for every variant before it writes anything, and computes from the snapshot so a repeat run cannot compound.