Skill

Handle n8n workflow failures with retries and structured alerts

Makes n8n failures loud and recoverable - per-node error outputs, retryOnFail self-healing, status-coded responses, and a workflow-level error catch-all.

Works with n8nwebhookslackgmaildiscord

80
Spark score
out of 100
Updated last month
Version 15.3.0

Add to Favorites

Why it matters

Prevent silent failures in unattended n8n workflows by routing errors to structured handlers, implementing bounded retries for transient issues, and ensuring API endpoints return proper status codes instead of timeouts or empty 500s.

Outcomes

What it gets done

01

Wire per-node error outputs to handlers so failures route to recovery logic instead of halting workflows

02

Configure retry-on-fail with bounded attempts and delays to self-heal transient network and API errors

03

Map error causes to correct HTTP status codes in webhook responses so callers receive actionable feedback

04

Validate API inputs with schema checks that return 400-level errors before processing begins

Install

Add it to your toolbox

Run in your project directory:

curl -fsSL https://spark.entire.vc/get/ag-n8n-error-handling | bash

Overview

n8n Error Handling

Makes n8n workflow failures loud and recoverable: per-node error output wiring, retryOnFail self-healing, cause-matched HTTP status codes, and a workflow-level Error Trigger catch-all. Use for unattended workflows, webhook/API response contracts, retry design, or any n8n path where failure must be visible and recoverable.

What it does

This skill makes n8n workflow failures loud, structured, and recoverable - ideally self-healing so transient blips never reach a human - instead of the default behavior where a thrown node halts the whole workflow silently: an interactive run shows a red node to fix, but an unattended workflow (webhook API, cron job, queue worker, agent tool) just gives the caller a timeout or empty 500 with no alert and no clue. Two ideas prevent most silent failures: per-node error outputs, where a node's failure routes down a second output you control instead of killing the run, and a workflow-level error workflow, a catch-all that fires for anything that escapes per-node handling (timeouts, crashes between nodes, unwired failures).

Error handling posture depends on workflow shape: webhook/API workflows require every fallible node's error output wired with a status code matching the cause; unattended workflows (scheduled/cron/queue/agent tool) require a workflow-level error workflow plus retryOnFail on network nodes; an internal one-off you run and watch yourself can rely on the default stopWorkflow behavior. The dividing line is whether anyone besides you sees the output.

The single most common way a workflow "handles" errors while actually swallowing them is that per-node error output is a two-step setup: setting onError to "continueErrorOutput" on the node (which creates the second output) and separately wiring that error output, sourceIndex 1, to a real handler. Doing only the first silently discards error data with the dashboard showing success; doing only the second leaves an unreachable handler and the workflow just halts. Because a half-wired error output validates clean, verification means pulling the workflow and confirming both halves are present - onError is "continueErrorOutput" and the connection's main[1] contains the handler. The three onError values are stopWorkflow (default, halts everything), continueRegularOutput (rare and usually wrong - error-shaped data flows out the normal output), and continueErrorOutput (the one you wire).

Before building error branches at all, self-healing comes first: setting retryOnFail, maxTries, and waitBetweenTries on any node calling a network service (HTTP Request, Gmail/Slack/Discord, databases, AI nodes, third-party integrations) absorbs transient failures like a 429 so the error output only fires on real, persistent problems. Engine limits: retry fires on any error with no per-status-code filter, maxTries caps at 5, and waitBetweenTries caps at 5000ms.

For webhook-triggered API workflows, the overriding rule is no hanging branches - every path, success and every error, must end at a Respond to Webhook or the caller times out. This works via fan-in to a single error responder, checking validation failures (4xx) upstream via IF/Switch or a schema validator rather than through error outputs (since a missing field is an expected outcome, not a node crash - error outputs are for unexpected 5xx failures), and always setting responseCode explicitly, since it defaults to 200 even on error branches - an error branch returning 200 with an error body looks like success to the caller's HTTP client. For structured input validation, a single Set node running an IIFE-based schema check (returning valid/validationError/details/requiredSchema) is faster and simpler than a chain of per-field IF/Switch nodes or a recursive Code-node validator.

Response shapes should map cause to status code rather than collapsing everything into one 500 internal_error, since the caller's monitoring alerts on 5xx (their fault to escalate) but not 4xx (the caller's own fault): validation errors are 400, missing/invalid auth is 401, authenticated-but-forbidden is 403, a valid-but-absent resource ID is 404, state conflicts are 409, exceeded rate limits are 429 with a Retry-After header, an unknown node failure is 500, a third-party API error is 502, a downstream-unavailable condition is 503, and a third-party timeout is 504 - 4xx gets decided before the work via IF/Switch, 5xx comes out of error outputs. When error paths differ only by status number and message with the same body shape, a single expression-driven Respond node computing the code inline is preferred over fanning out through a Switch to N Respond nodes; Switch-plus-multiple-Responds is reserved for structurally different paths (different headers, body shapes, redirects). The default response envelope is {error, message} with no ok:false flag since the HTTP status already conveys success/failure, and internals (stack traces, SQL, upstream bodies, tokens) must never leak into the response - log those privately instead.

The workflow-level error workflow is the safety net for everything per-node handling misses: it's a separate workflow starting with an Error Trigger node, invoked with execution and error context, and a minimal version is capture-then-notify (Error Trigger to Set to Slack/email). A good alert includes the workflow name, editor and execution links, the failed node name, and the real error message. Two traps: the recursion trap, where notifying the same channel a monitored workflow uses means that channel going down also kills the error workflow's own notification (fix: notify on a different channel, plus a Data Table fallback so a failed notification still leaves a trace); and the fact that a "handled" error (routed to a no-op that drops the data) is considered handled by n8n and the error workflow will not fire for it. Assigning a workflow's Error Workflow setting is explicitly UI-only (Workflow Settings) with no MCP tool for it - the MCP can build the error workflow, set onError/retryOnFail, wire error outputs, validate, autofix, test, and inspect failures, but cannot assign the error-workflow setting or toggle other workflow settings like Save Execution Data, timezone, or timeout.

Eleven documented anti-patterns cover: onError set without a wired error output; error output wired without onError set; a webhook workflow with no error branch at all; an error branch returning 200 with an error body; one blanket 500 for every failure; catching errors in a Code node and returning them as data instead of throwing; a network node missing retryOnFail; a Switch fanning out to N Responds that differ only by status code; an unattended workflow with no error workflow at all; an error workflow that notifies the same channel it's monitoring; and leaking $json.error internals into a caller-facing response.

When to use - and when NOT to

Use for unattended workflows, webhook/API response contracts, retry design, error outputs, Error Trigger workflows, alerting, or any path where failure must be visible and recoverable. Make retries bounded and idempotent, especially for sends, payments, and writes; redact credentials, personal data, request bodies, and stack details from caller-facing responses and alerts, exposing only the minimum diagnostic context required.

Inputs and outputs

Input is an n8n workflow with fallible nodes (network calls, database writes, AI calls) and, for API workflows, a webhook trigger. Output is a workflow where every failure path either retries transparently, routes to a wired error handler with a cause-matched status code, or escalates through a workflow-level Error Trigger with a real, actionable alert.

// 1) Turn on the error output (creates main[1])
{ type: "updateNode", nodeName: "HTTP Request",
  changes: { onError: "continueErrorOutput" } }

// 2) Wire the error output to a handler. sourceIndex: 1 = the error output.
{ type: "addConnection",
  source: "HTTP Request",
  target: "Handle Error",
  sourceIndex: 1 }

Integrations

n8n-workflow-patterns (the webhook/API and scheduled shapes this skill hardens), n8n-node-configuration (onError/retryOnFail as node config, response-code traps), n8n-validation-expert (a half-wired error output isn't a validation error, this skill is the fix), n8n-expression-syntax (expression-driven Response Code and alert messages), n8n-code-javascript/n8n-code-python (deciding whether to re-throw or handle-and-continue inside a Code node), n8n-code-tool (an agent's Code Tool has a different error contract - thrown errors go back to the LLM), and n8n-binary-and-data (file/binary operations need wired error outputs too).

Who it's for

n8n workflow builders running unattended workflows, webhook APIs, or agent tools who need failures to be visible, correctly status-coded, and recoverable instead of silently swallowed or halting with no alert.

FAQ

Common questions

Discussion

Questions & comments · 0

Sign In Sign in to leave a comment.