What happens when an automation runs the same job twice
A workflow fails halfway through. It tries again. The second attempt finishes cleanly, the log goes green, and nobody looks any further.
Somewhere on the other side of that green tick, a customer has two invoices for one order. Or two identical parcels. Or a card charged twice for the same thirty pounds.
This is not the same problem as an automation that breaks and says nothing. That one is about detection - a workflow stops working, and you find out from a client three weeks later. This one is the opposite shape. Everything is detected, everything is reported as fine, and the damage happens during the recovery. The repair is what does it.
The retry is a default. The deduplication is not.
Almost every tool in this space retries on your behalf, and almost none of them stop the action from happening twice. Those are two separate features, and only the first one is switched on when you buy it.
Here is what the common tools do when a step fails, taken from their own documentation:
| Tool | Retries by itself? | How many, over how long | Stops duplicates? |
|---|---|---|---|
| Zapier (Professional and up) | Yes, if autoreplay is on | 5 attempts, the last about 10 hours 35 minutes after the first error | Only on the trigger side, not on actions |
| Make.com | Yes, for rate-limit, connection and timeout errors | 8 attempts over roughly 7 hours 51 minutes | No |
| n8n | No, off per node until you enable it | Your setting | No, though a Remove Duplicates node exists |
| Twilio webhooks | Yes | 1 retry by default, configurable up to 5 | An idempotency token in the header |
| Shopify webhooks | Yes | 8 retries over 4 hours | You deduplicate on the webhook ID |
| Stripe webhooks | Yes | Up to 3 days | You deduplicate on the event ID |
| Amazon SQS | Yes, inherently | at-least-once delivery | None. Amazon's instruction is to "design your applications to be idempotent" |
Read the last column again. The transport layer retries. The business action - the invoice, the charge, the text message - has no idea it is the second attempt. Nothing in that table is a bug. Every one of those retry ladders is a sensible default that recovers from a dropped connection without waking anyone up. The gap is that recovery and safety were sold as one feature and shipped as one and a half.
Amazon says the quiet part directly in its Lambda documentation: your code should expect to "handle the same event multiple times". Google says the same about Pub/Sub, which may redeliver a message "even after an acknowledgment request... returns successfully". These are not warnings about failure. They describe normal operation.
The failure and the damage are two different problems
Worth separating clearly, because they need different fixes.
An automation that fails silently is a detection problem: the workflow stopped, the green tick lied, and nobody noticed. That is the subject of why automations fail silently, and the answer there is alerting - heartbeats, checks on the result rather than the run, somebody who gets told.
A retry that repeats a real action is a design problem. Here the alerting works perfectly. The system noticed the failure, told nobody because it did not need to, fixed itself, and in fixing itself sent your customer a second invoice. More monitoring does not help. What helps is the action being built so that running it twice has the same effect as running it once.
The two do overlap in one place. Both get worse the longer the automation runs without anyone looking at it, and both are invisible from inside the tool that caused them.
What it looks like from the customer's side
The abstract version of this is a duplicate row in a database. The real version is more expensive.
A shop finds out from its customers. Half a dozen of them, over a couple of months, ring to say they received their order twice, with one shipping label on record for each. Not everyone who receives a second parcel is honest enough to send it back, and the ones who are still cost a return. The duplicate was not a record. It was stock that left the building.
The clearest description of the mechanism comes from an n8n user in March 2026, and it fits in one breath:
"We kept running into the same issue with n8n webhooks: Stripe webhook fires -> workflow runs -> charge executes. Then Stripe retries the webhook -> workflow runs again -> charge executes again. Customer gets charged twice."
The scale can be worse than one customer. An endpoint that fails to return a success code is redelivered the same event on a schedule, hourly on some platforms, and if the endpoint credits the customer's account each time, two real charges become two dozen stored events and two dozen credits. Restarts do it too: a marketing server comes back up, the campaign re-triggers, and a few thousand text messages go out a second time. Nobody misconfigured anything. A process came back up.
Two accounting versions, since that is where it costs most quietly. A form with three line items, and a looping step inside the automation that runs the invoice action once per item, produces three invoices in the ledger where there should have been one. And Intuit's own documentation on its QuickBooks connector warns that changing a start date "may risk re-syncing old data and cause duplications" - the vendor knows the replay is not protected.
A green log is not evidence
The instinct after a scare like this is to open the automation and count the runs. That is the wrong instrument.
Stripe's documentation concedes the case plainly: if creating a charge returns a 500 error, the information may already have gone out to the payment network, and "you may still see requests with a 500 error that produce user-visible side effects". A step showing red in your log can have taken the money. A step showing green can be the second one.
The redelivered-event case makes the same point from the other direction. The payment dashboard shows two charges. The application shows two dozen credits. Both are accurate about their own side.
So the count that settles it is never in the automation tool. It is at the destination: the customer's inbox, the ledger, the card statement, the shipping labels you paid for. When somebody tells you a duplicate is impossible because the run history shows one execution, they are reading the instrument that cannot see the problem.
"We added a duplicate check" is not the same as safe
This is the part that surprises people who have already thought about the problem once.
A Zapier user in February 2026 built exactly the guard you would build. Store a key when the job runs; check the key next time; skip if it is set. His description:
"We store a key in storage and if its value is 1, then we skip that otherwise continue and set the value of key to 1 so next time its skipped."
What happened:
"In Zap history runs, it shows that filter successfully skipped second run, but in Gmail we are still receiving same AI generated email twice."
The filter was right. The email went twice anyway. The thread never settles on a cause - a Gmail labelling loop is the leading theory there - but the read-then-write guard is a known way to get exactly this outcome: two runs read the key before either has written it, both see an empty value, both proceed, and one records itself as skipped afterwards. The check and the write are two separate steps, and the gap between them is where the second run lives.
This is why the answer is not a lookup step in a workflow builder. The check and the claim have to be a single operation at the place the work actually happens. That is a property of the destination system, not of the automation on top of it - which is the practical reason this belongs in a conversation with whoever builds the thing, before it is built.
Worth saying the fair version too: platforms do offer real protection, and some of it is on by default. Zapier deduplicates incoming trigger data so the same source record does not start two runs. That is genuine and useful. It just does not touch the risk described here, because it governs what starts the workflow rather than what the workflow does, and it resets when the Zap is toggled off.
Five things to ask the person building it
None of these require you to read code. Each one has an answer you can be shown.
1. "Where is the reference number generated, and what happens if the same one arrives twice?" Every write carries a unique reference created before the first attempt. If it turns up again, the receiving system returns the original answer instead of doing the work a second time. Stripe, Xero and QuickBooks Online all support this. A good answer contains the words same result. A bad one contains the word error, and a worse one is a blank look.
2. "Where is the list of things we have already handled, and how long does it keep them?" For anything arriving from outside - a payment event, a store webhook, a message callback - the automation should write down the event's identifier and check that list before acting. Stripe's own instruction is "logging the event IDs you've processed, and then not processing already-logged events". If the answer is "in memory" or "for the current run", there is no list.
3. "Which field decides that this is the same customer?" A step that says create contact gives you two Jane Smiths when it runs twice. A step that says create or update contact, matched on email gives you one. HubSpot and Salesforce both support matching on a chosen field. If nobody can name the field, nothing is matching on anything.
4. "What does the automation look at to decide the invoice already went out?" Every record should carry a state - pending, sent, paid, failed - and the step should refuse to act on one already marked done. The answer should be a field you can open and look at, not an assurance that it does not happen.
5. "After how many failures does it give up, and who gets told?" Retrying forever is not resilience. It is a machine repeating a broken action while nobody is informed. There should be a limit, and past that limit the job should land somewhere a person sees it.
One more, cheaper than all five: ask what the retry settings actually are. Five attempts over ten and a half hours is a very different Tuesday morning from one attempt and a stop. Those numbers exist in your account today, and almost nobody is told them at handover - which is the sort of thing worth pinning down in the handover itself, alongside the question of who unregisters an automation's webhooks when you retire it. Users chasing double executions have blamed leftover registrations from workflows they thought were gone.
The windows that do not line up
There is a wrinkle here that even careful builds miss, and it is worth knowing because it explains failures that look impossible.
Protection by reference number has an expiry date, and every vendor picked a different one. Stripe keeps a key for 24 hours. Adyen keeps one for a minimum of seven days. Xero expires them after six minutes.
Now put those next to the retry ladders from the first table. Zapier's last attempt lands about ten and a half hours after the first error. Make's runs for close to eight hours. Stripe's own webhook retries continue for up to three days.
A retry that arrives after the key has expired is not recognised as a repeat. It is treated as a fresh request, and it creates a fresh record. Six minutes of protection against ten hours of retrying is not protection; it is a coincidence that usually holds. No vendor documents the mismatch, because no vendor owns both halves.
This is worth asking about specifically when money or accounting is involved, because that is where the short windows happen to sit.
Before you sign anything off
The short version, for a build that already exists: pick the three actions that would hurt most if they happened twice - the charge, the invoice, the message to a customer - and ask what stops each one. Not what detects it afterwards. What stops it.
If the answer for all three is a filter step inside the automation, you have the Zapier user's guard, and it will hold until the day two runs overlap.
Some of this is also a reason not to automate a step at all. An action that happens four times a month, cannot be undone, and would embarrass you in front of a customer is often cheaper left in a person's hands - the same calculation about what not to automate applies here, with the retry behaviour as one more input.
If you would rather have someone go through your existing automations and work out which ones can repeat themselves, that is what the process audit is for: $299, three business days, and a written account of what to fix first.
Sources
Retry behaviour and defaults come from vendor documentation: Zapier on replay and its retry schedule, Make on automatic retry of incomplete executions, n8n's Retry On Fail node setting, Twilio's webhook retry and idempotency token, Shopify's webhook retry policy, and Stripe's webhook retry schedule.
On why duplicates are unavoidable at the transport layer and must be handled at the destination: AWS Lambda's guidance to handle the same event multiple times, Amazon SQS on at-least-once delivery, and Google Pub/Sub on redelivery after acknowledgement.
Idempotency mechanics and expiry windows: Stripe's idempotent requests documentation, Stripe's engineering post on idempotency, Xero's idempotency keys, QuickBooks Online on RequestId, and Adyen's idempotency documentation.
The cases quoted here are public posts by the people they happened to: the Stripe retry that charged twice, and the filter that reported skipped while the email went twice.
Intuit's warning about re-syncing old data is in its QuickBooks Connector sync settings guide.