DEV Community

Viet Automation
Viet Automation

Posted on

Does n8n's "Retry execution" Repeat the Failed Node's Action? Idempotency Keys for Safe Retries (2026)

Short answer: yes, the failed node runs again. When you retry a failed execution, n8n restarts from the node that failed and reuses the stored output of the nodes before it, so earlier nodes are not re-run but the failed one is. That is safe when the failed call truly did nothing. It is not safe when the call reached the other side and only the response failed (a timeout, a 502 from a proxy, a dropped connection). Then the retry sends the email, charges the card or creates the CRM record a second time. The fix is an idempotency key checked before every external side effect.

The n8n docs on retrying failed executions say it plainly: the failed node receives its saved input again and processes every item, including items it handled before it failed. This is one of the most common ways "we sent it twice" bugs happen in production workflows.

Two different retries

Retry type Where you set it What runs again
Node setting Retry On Fail Node settings Only that node, automatically, inside the same execution
Retry execution (from the Executions list) Manual, after a failed run From the failed node onward, reusing earlier nodes' saved data

Both repeat the failed node's call. Neither knows whether the remote system already acted.

The failure that causes duplicates

  1. The HTTP Request node sends POST /invoices.
  2. The API creates the invoice, but the response takes longer than your timeout.
  3. n8n marks the node as failed.
  4. Retry On Fail, or you clicking Retry, sends POST /invoices again. Now there are two invoices.

Pattern: claim a key before the side effect

Build a stable key from the business event, not from the execution, for example invoice: plus the deal ID. Store it in one table and claim it with a single statement:

CREATE TABLE IF NOT EXISTS side_effects (
  idem_key   TEXT PRIMARY KEY,
  status     TEXT NOT NULL DEFAULT 'pending',  -- pending | done
  result     JSONB,
  claimed_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Claim (Postgres node, before the external call)
INSERT INTO side_effects (idem_key) VALUES ($1)
ON CONFLICT (idem_key) DO UPDATE
  SET claimed_at = now()
  WHERE side_effects.status = 'pending'
    AND side_effects.claimed_at < now() - interval '10 minutes'
RETURNING idem_key;
Enter fullscreen mode Exit fullscreen mode
  • A row comes back: you own the key, so make the call.
  • No row: either it is already done (skip and reuse result) or another run claimed it in the last 10 minutes (stop).

One catch: a manual Retry execution resumes from the failed node, so a claim node that already succeeded earlier in the same workflow is not run again. Put the claim, the external call and the done update together in a small sub-workflow called with Execute Sub-workflow. Per the docs, when the failed node is an Execute Sub-workflow node, the retry runs the whole sub-workflow again from its start, so the claim is checked again before the call is repeated.

After the call succeeds:

UPDATE side_effects SET status = 'done', result = $2 WHERE idem_key = $1;
Enter fullscreen mode Exit fullscreen mode

Pass the key to the API too

Many APIs accept an idempotency header (Stripe's Idempotency-Key is the best-known example). When the API supports one, send the same key. That covers the timeout case completely, because the remote side deduplicates even if your done update never ran. When the API has no such header, search before you create (look up the record by your external ID) for the risky steps.

Checklist before turning on retries

  • Every node that sends, charges, creates or posts has a business-event key.
  • The key is claimed in the database before the call, not after.
  • Retry On Fail is on only for nodes that are read-only or protected by a key.
  • Timeouts are longer than the API's normal worst case.
  • A stuck pending row older than the lease window is surfaced to a human, not silently retried forever.

Same idea in Make

Make has its own error handlers and incomplete-execution retries, and the same rule applies: a retried module repeats its call, so put a Data Store lookup on your business key in front of any module with side effects.

If you want to compare how Make handles this, you can try it here: Make.


Disclosure: Some links are affiliate links — I may earn a commission at no extra cost to you.

Top comments (0)