A common production problem: a run fails halfway, you retry it, and the customer gets the same email twice or the CRM gets a duplicate record. Retries are good, but only if every step is safe to run again. Here’s a simple pattern that makes them safe.
1. Give every item a stable key
Build the key from the business data, not from the execution. For example: lead email + form ID, or order ID + action name (“order_1042_welcome_email”). A retried or resumed run produces the same key.
2. Check before you act
Before any side effect (send email, create record, post a message, charge a card), look the key up in a small log table: an n8n Data Table, Postgres, Google Sheets or Redis all work. If it’s already marked done, skip it with an IF node.
3. Mark done right after the action succeeds
Order matters: action first, then write the log. If you write the log first and the action fails, the item is skipped forever without anyone noticing.
4. Prefer upserts over creates
Most CRMs and databases support “create or update” by an external ID. Use it whenever you can, and duplicate records mostly disappear on their own.
5. Treat errors differently
- Timeouts, 429 and 5xx = recoverable. Use Retry On Fail with a wait between tries, or a loop with backoff.
- Validation errors = not recoverable. No retry; route the item to a “needs review” list.
- Anything unexpected = an Error Trigger workflow that alerts you with the execution link.
6. Make re-runs boring
With steps 1 to 3 in place, you can re-run a failed execution from the start. Steps already logged as done are skipped, so you never have to work out where it stopped.
I use the same idea in a reporting workflow: access is re-checked right before sending, each report gets a single send attempt, and every delivery is logged so it can be audited later.
How do you handle this in your workflows? Do you keep the log in a Data Table, or in an external database?