The first workflow takes an hour. An invoice arrives by email, Claude extracts amount and supplier, a record lands in the ERP. The hundredth workflow decides whether automation works in your company. At some point a supplier changes its PDF layout, the ERP API answers with a 503, and nobody notices. Here is how we build error handling in n8n so that errors become visible instead of disappearing.
A silent failure is worse than no workflow
Without automation, employees book invoices by hand. Slow, but someone sees every invoice. With a workflow that has been failing silently for three weeks, nobody sees anything, and everyone relies on it. The damage shows up when the supplier sends a reminder or at month-end close.
The ground rule: every error has to arrive somewhere a human sees it. The rest of this article is technique to enforce that rule.
Error workflows: the foundation
n8n lets you assign an error workflow to every workflow. When an execution fails, it starts via the Error Trigger and receives workflow name, execution ID, failed node and error message.
Our standard is one central error workflow per instance that
- writes the error data to a table,
- decides who gets notified based on the workflow tags,
- sends a message to Slack or Teams with a link to the execution.
The error workflow is mandatory. No workflow goes to production without one, and we check that with a script against the n8n API. Two things surprise people in reviews: the error workflow only fires for automatic executions, not for manual test runs. And it does not fire when a node swallows the error via "Continue on Fail".
Retries: configured properly, not everywhere
Every node has "Retry On Fail" in its settings, with attempts and wait time. That helps with transient errors: timeouts, 429, 503. It does not help with a wrong API key or a missing required field. A retry only delays the failure there.
Our rules:
- Retry on HTTP nodes against external APIs: three attempts with a wait. n8n limits the wait to a few seconds. If an API is down for half an hour, use the dead-letter pattern below.
- No retry on nodes that write, unless the write is idempotent. Otherwise the invoice ends up in the ERP twice.
- "Continue using error output" only where you actually wire up the error output. A node with "Continue" and no connected error output is the most common silent failure we find in reviews.
- For Claude calls, retry on rate limits, but also validate the response. A JSON object with a missing field is an error too, just without a status code.
Idempotency: retries without double bookings
Retries are only safe when every step may run more than once without duplicate effects. In practice:
- Every item gets a business key: invoice number plus supplier, order ID, or the message ID of the email.
- Before writing, check whether a record with this key exists. Then update or skip, never create twice.
- Where the target API supports it, send idempotency keys. Almost all payment APIs do.
- Side effects such as emails go at the end, after all writes. Record in the table that the email went out.
An idempotent workflow can simply be restarted after a failure. "Retry" in the UI becomes a safe action.
Dead-letter pattern: park failures instead of losing them
Some failures do not resolve through retries: the PDF is unreadable, the customer is missing in the ERP, the API is down for hours. These cases belong in a dead-letter queue: a place where failed items wait until someone or something picks them up.
Two implementations work well:
- A Postgres table with workflow, key, payload, error, attempts and status. The error workflow writes into it. A scheduled workflow retries open entries hourly, up to a limit. After that, status "manual" and a message to the business unit.
- An SQS queue on AWS with a dead-letter queue attached, if your workflows are triggered via queues anyway. SQS handles visibility timeout and attempt counting. n8n reads it with the SQS node.
Our table looks like this:
CREATE TABLE failed_items (
id bigserial PRIMARY KEY,
workflow text NOT NULL,
item_key text NOT NULL,
payload jsonb NOT NULL,
error text,
attempts int DEFAULT 0,
status text DEFAULT 'open',
created_at timestamptz DEFAULT now(),
UNIQUE (workflow, item_key)
);
It doubles as your report: which workflows fail how often, and why. That tells you which workflow to harden next.
Alerts with owners and clear names
An alert without a recipient is a log line. So:
- Every workflow has an owner, named in the workflow description and in an overview the business unit knows.
- One channel per department, not one for everything. The finance channel goes to accounting and the IT owner. A catch-all channel gets muted within two weeks.
- The message contains workflow, item key, the error in one sentence, a link to the execution, and what to do. "Supplier Miller is missing in the ERP, please create it" helps. "Error in node HTTP Request" does not.
- Plus a daily digest: executions, failures, open dead-letter entries. A missing digest is an alert in itself, because then the instance is down.
Naming conventions belong here because they make alerts readable. We use [Department] What happens, for example [Finance] Incoming invoice to ERP or [HR] Onboarding create accounts, plus tags for owner and criticality. Whoever reads a message at three in the morning should know whether it can wait.
Takeaway
The first workflow runs in an hour. Whether the hundredth still runs is decided by error workflows, retries with idempotency, a place for failed items and alerts with owners. That is less work than it sounds if you build it in from the start. If you want to bring your n8n workflows to production grade, book a free 30-minute intro call.