Retry from the failed node: does it repeat the failed node's external effect, and how do you guard against it?

Hi all. I’m researching recovery after partial failures in n8n and want to confirm behavior beyond the docs.

The docs (Understand executions) say a retry resumes from the saved execution state instead of starting again from the trigger, completed nodes keep their results, and the failed node processes every item again, so a node that sent some messages before failing sends them again. They also say a failed Execute Sub-workflow node reruns the whole sub-workflow.

What I’d like to confirm from experience:

  • Single call that reaches the service but returns an error or times out: is the action performed twice on retry?
  • Multi-item node that fails on item 3: are items 1 and 2 repeated on retry?
  • Public API retry (POST /executions/{id}/retry, loadWorkflow true or false): same restart point as the UI?
  • Error Workflow that triggers a retry: any way to cap it at one attempt?
  • Retry On Fail node setting versus the execution retry: same effect on repeated actions?

Please say which n8n version and mode (regular or queue) you tested on, and whether you observed it or are quoting docs. Any guard pattern you use (idempotency key, existence check before the action, processed-ids table) would also help.

Thanks.

Hey @faridd692, while you wait for a response, here are some things that might help:

Suggested resources

Automatically matched to your question.

Docs:

Forum:

@Joy_Ful, @Emmas, @Niffzy - you’ve helped with similar issues before, can you take a look?

Automatically suggested by n8n’s community bot. It’s a pilot - please share feedback here.

Hi @faridd692 welcome to the n8n community.
Yes, retrying a timed-out call can duplicate the action if the service committed it before the timeout. I’m quoting the n8n@2.45.7 source here, not reporting a regular/queue reproduction. For an ordinary HTTP/message node that stops on error, the failed invocation receives its input again, including items 1 and 2. Retry On Fail repeats within the same execution; execution retry creates a new execution using the saved state.

The UI and public API call the same retry service. loadWorkflow: true replaces the workflow/node definitions used with that saved state; false keeps the original definitions. It doesn’t switch the restart point to the trigger.

In your Error Workflow, call retry only when execution.id exists and execution.retryOf is absent. If that retry fails, alert instead of retrying it again. Keep Retry On Fail disabled if you want only one extra node attempt. Use a provider-supported idempotency key per intended action, for example the original event ID plus action and recipient/item, and reuse it on every attempt. Don’t base that key on $execution.id, which changes on execution retry. A separate existence-check node before the failed node won’t run again before that retried invocation.

Handle errors gracefully | Build | n8n Docs

Hi @faridd692

Adding a reproduction to @Deepankar’s explanation: I tested n8n 2.45.7, regular mode, SQLite, HTTP Request 4.2, against a localhost mock service. Queue mode wasn’t tested.

Observed results:

  • The service recorded a write, then returned 500 or timed out: execution retry produced a second write.
  • Failure on item 3: the receiver logged 1,2,3,1,2,3. The completed upstream node ran only once.
  • With an unchanged graph, public API loadWorkflow: true, false, and the UI’s retry endpoint preserved that same restart point.
  • Retry On Fail with maxTries: 2 repeated the batch within one execution. Execution retry created a new execution.
  • A failed Execute Sub-workflow node reran the child, including its already completed marker.
  • An Error Workflow checking that execution.id exists and execution.retryOf is absent allowed one additional execution. The failed retry invoked the handler again, but the IF stopped another retry. Concurrent retry callers still need an atomic claim.

Having the mock receiver enforce the same per-action idempotency key gave two requests but only one write.

One additional 2.45.7 detail: I had to publish the Error Workflow before it ran. The unpublished handler produced no handler executions; this version’s loader requires published workflow data.

One case I’d explicitly model is an ambiguous outcome: the external API times out, but the write may already have committed

I’d keep that operation in an “outcome unknown” state rather than immediately marking it failed. Recovery would first reconcile against the provider using a stable business reference, then retry only if it’s safe. If the provider has no reliable lookup or idempotency support, I’d route it for review instead of risking a duplicate CRM update or outbound action

That distinction between execution failure and business-operation failure matters a lot in production workflows

@faridd692 on the queue mode part, I read the worker side at n8n@2.45.7 (source again, not a queue reproduction). The worker loads the retry execution from the database and runs it from the same saved node stack, so the restart point and the repeated items are the same as in regular mode.

The two queue-only events don’t replay a finished call either. Bull is set up with maxStalledCount: 0, so a worker that dies mid-node fails the job rather than passing it to another worker, and a worker won’t run an execution that’s already marked crashed. A graceful worker shutdown can park a running retry, but only at a node boundary: the current node finishes and another worker carries on from the next one.

Once you’ve confirmed the re-send, are you leaning toward lookup-before-create on that node, or moving the write behind an idempotency store outside n8n so a resume can’t double-hit?