Learn Build Community Opportunities Hosted n8n Blog About Careers Contact Log in

Home / Blog / Fix

Fix

N8n workflow runs twice: practical fixes

Concrete, copy-paste idempotency patterns to stop duplicate side effects when your n8n workflow runs twice.

If your n8n workflow runs twice, the quickest fix is to add an idempotency key and a single-row DB check so duplicate requests are ignored. Use a header like Idempotency-Key, insert it into a table with a unique constraint, and only process when the insert succeeds.

Why your n8n workflow runs twice

There are three common causes: the sender retries because your webhook timed out or returned non 2xx, two identical webhooks registered for the same route, or transient infrastructure retries from load balancers. The result is duplicate side effects like double invoices, duplicate SMS or duplicate records.

See community reports for how retries and retries-without-idempotency break workflows: Your n8n workflow runs twice and nobody notices and A retry needs to know what already happened.

Concrete idempotency patterns you can copy-paste

  1. Require a request dedupe key. Have the caller set an Idempotency-Key header or include a stable request_id in the body. If the caller can not be changed, compute a digest of the body in a Code node.
    // Code node (JavaScript) to make an idempotency key
    const crypto = require('crypto');
    const body = $json.body || $json;
    const provided = $input.all()[0].json.headers && ($input.all()[0].json.headers['idempotency-key'] || $input.all()[0].json.headers['Idempotency-Key']);
    const key = provided || crypto.createHash('sha256').update(JSON.stringify(body)).digest('hex');
    return [{ json: { idempotency_key: key, payload: body } }];
    
  2. DB backed uniqueness check. Create a small table with a unique key and use an atomic insert to decide whether to process. Example Postgres table and SQL to use in the Postgres node.
    CREATE TABLE processed_requests (
      idempotency_key text PRIMARY KEY,
      payload jsonb,
      created_at timestamptz DEFAULT now()
    );
    
    -- SQL to run from n8n Postgres node (use parameter expressions)
    INSERT INTO processed_requests (idempotency_key, payload)
    VALUES ('{{ $json["idempotency_key"] }}', '{{ $json["payload"] | json }}')
    ON CONFLICT DO NOTHING
    RETURNING idempotency_key;
    

    If the Postgres node returns a row, continue processing. If it returns no rows, stop the workflow because this request is a duplicate.

  3. Webhook acknowledgement strategy. Configure the n8n Webhook node to respond quickly so the sender does not retry. Use the Webhook node Response Mode set to On Received, or add a Respond to Webhook node as the first response. If you need async processing, return HTTP 202 rapidly and continue the heavy work after the DB dedupe.
  4. Guard long running tasks. For workflows that call external APIs with retries, perform the DB insert before outbound calls. That way retries will not re-trigger expensive actions. Use branch logic: check insert then call HTTP Request or other nodes only when insert succeeded.
  5. Optional: distributed locks. If you cannot rely on INSERT semantics, use a small Redis lock with SETNX and an expiry, or a Postgres advisory lock if you are comfortable with SQL functions. These are advanced options for parallel servers and heavy concurrency only.

Step by step fix you can paste into a workflow

  1. Add a Webhook node that receives the request. Set Response Mode to On Received so the webhook returns immediately.
  2. Add a Code node to extract or compute idempotency_key using the snippet above.
  3. Add a Postgres node. Use the INSERT ... ON CONFLICT DO NOTHING SQL shown above. Have this node return the inserted row.
  4. Add an If node. Condition: Postgres returned rows? If true, continue to your business logic. If false, stop and optionally log the duplicate.
  5. Process normally. Any retries will hit the Postgres check and be skipped.

Tool specific notes for n8n, Make.com and Zapier

n8n

Use the Webhook node, the Code node for key generation and the Postgres or MySQL node for dedupe. Keep the insert-first pattern: insert the idempotency key, then run HTTP Request or other side-effecting nodes. For long tasks set Response Mode to On Received or add Respond to Webhook at the start.

Make.com

Make.com scenarios have a Webhook module and a Data Store. Require the caller to send Idempotency-Key and use the Data Store or a small external DB to store keys. The pattern is the same: store then process, otherwise stop the scenario.

Zapier

Zapier triggers often provide an ID that can be used for dedupe. If you need custom dedupe, use Storage by Zapier or an external database. Implement an insert-or-ignore before side effects so retries do not double-run actions.

How to stop duplicates happening again

  • Require an Idempotency-Key from producers. Make this part of API docs or webhook setup in Nigeria-based clients and vendors.
  • Make the dedupe table tiny and fast, add a TTL cleanup job if needed to avoid indefinite growth.
  • Monitor duplicates with a small dashboard or alert when an INSERT returns zero rows unexpectedly often. See our post on alerts for n8n failures for monitoring ideas: N8n alert when workflow fails.
  • Test failure modes: simulate slow responses, simulate the sender retrying after 5 seconds, and confirm only one side effect occurs.
  • When you cannot change the sender, rely on server-side computed keys (digest of payload) and stricter insert checks.

When to get help

Get help if duplicates persist after you add idempotency keys and DB checks. Persistent duplicates can come from multiple webhook registrations, proxy retries, or a misconfigured load balancer. If your infrastructure is complex or you have high throughput, consider professional help to design distributed locks and scalesafe dedupe.

Community threads are useful for debugging behaviour and edge cases: read the discussion on duplicate runs and idempotency patterns on the n8n forum for practical examples and gotchas: n8n forum idempotency thread.

Most fixes are small and local: add a single unique-key table, compute or require an Idempotency-Key, and acknowledge webhooks quickly. That will stop the majority of duplicate runs.

Learn to build automations like this with Automate Naija.

Frequently asked questions

Why does my n8n webhook sometimes trigger the same workflow twice?

Most often the sender retries because your webhook took too long or returned a non 2xx code, or you have two routes registered for the same webhook URL. Network retries from gateways or load balancers can also replay requests. The fix is an idempotency key plus a DB insert check so repeats are ignored.

What is an idempotency key and how do I add it in n8n?

An idempotency key is a unique value for each logical request, sent as an Idempotency-Key header or generated by hashing the request body. In n8n use a Code node to extract or compute the key, then store it in a DB with a unique constraint before running side effects.

Can I rely on n8n to dedupe automatically?

No, n8n does not automatically dedupe business side effects. You must implement idempotency in your workflow, typically by checking a DB or external store before executing actions that must not repeat.

What SQL pattern should I use to ignore duplicates?

Use an atomic insert with a unique constraint. For Postgres: create a table with idempotency_key text PRIMARY KEY and run INSERT INTO processed_requests (...) VALUES (...) ON CONFLICT DO NOTHING RETURNING idempotency_key . Continue only if you get a returned row.


Build it with us. The community is free and the build track teaches this kind of automation week by week: see pricing or join the community.

Keep reading

A new fix lands here every morning, picked from what builders are stuck on right now.