DEV Community

Pirate Prentice
Pirate Prentice

Posted on

n8n Error Handling: How to Catch Errors and Build Resilient Workflows (+ Free JSON)

Most n8n tutorials show the happy path. But real workflows fail — APIs go down, webhooks return unexpected data, rate limits kick in. If you haven't wired up error handling, your workflow silently dies and you have no idea what broke.

This guide covers every error handling tool n8n gives you, when to use each one, and includes a free workflow JSON you can import and adapt.


The 3 layers of n8n error handling

  1. Node-level — Stop On Error / Continue On Error settings per node
  2. Workflow-level — Error Workflow (a separate workflow that fires when yours fails)
  3. Branch-level — IF node or Switch node to route errors explicitly

Most people only use layer 1. All three together make your workflows truly resilient.


Layer 1: Node settings — Continue On Error

Every n8n node has an On Error setting (in the node's Settings tab):

  • Stop Workflow (default) — any error halts the entire run
  • Continue (using error output) — the node outputs the error as data and execution continues
  • Continue (ignoring error) — silently skip items that error, continue with the rest

When to use Continue (using error output):
When you want to handle the error downstream — route failed items to a Slack alert, a Google Sheet log, or a retry queue.

When to use Continue (ignoring error):
For non-critical steps where partial success is fine. Example: enriching contact data from a third-party API — if one lookup fails, skip it and continue processing the rest.


Layer 2: Error Workflow

The Error Workflow is a separate n8n workflow that fires automatically whenever any workflow in your instance fails with an unhandled error.

How to set it up:

  1. Create a new workflow — add an Error Trigger node as the starting node
  2. Wire it to your alert action (Slack message, email, Google Sheet entry)
  3. Go to Settings → n8n Settings → set "Error Workflow" to your new workflow

The Error Trigger node gives you:

  • {{ $json.workflow.name }} — which workflow failed
  • {{ $json.execution.id }} — link to the failed execution
  • {{ $json.error.message }} — the error message
  • {{ $json.error.node.name }} — which node failed

Example Slack alert:

Workflow failed: {{ $json.workflow.name }}
Node: {{ $json.error.node.name }}
Error: {{ $json.error.message }}
Execution: https://your-n8n-instance/executions/{{ $json.execution.id }}
Enter fullscreen mode Exit fullscreen mode

This is the single highest-leverage error handling step you can take. One Error Workflow covers every workflow in your instance.


Layer 3: Branch-level error routing with IF node

Sometimes you want finer control — handle specific error types differently, or retry before alerting.

Pattern: Retry once, then alert

  1. HTTP Request node → Continue (using error output)
  2. IF node: {{ $json.error }} exists → true branch = retry path, false branch = success path
  3. On the retry path: Wait node (30s delay) → same HTTP Request again
  4. Second failure → Slack alert

Pattern: Route by HTTP status code

IF: {{ $json.statusCode }} === 429
  → Wait 60s → retry
IF: {{ $json.statusCode }} === 401  
  → Alert "credentials expired"
IF: {{ $json.statusCode }} >= 500
  → Alert "upstream API down"
Enter fullscreen mode Exit fullscreen mode

Common mistake: not logging enough context

When an error alert fires at 3am, you need enough context to diagnose without replaying the execution. Always log:

  • The input data that caused the error (first 500 chars)
  • The node name
  • The full error message
  • A direct link to the execution
  • Timestamp

Use a Set node before your alert node to assemble these into a clean object.


Free workflow JSON: Error handling starter

This workflow demonstrates all three layers:

  • An HTTP Request with Continue On Error enabled
  • An IF node routing errors vs. successes
  • A mock Slack alert for the error path
  • Comments explaining each choice

Import it: n8n menu → Import from JSON → paste below

{
  "name": "Error Handling Starter",
  "nodes": [
    {
      "parameters": {},
      "id": "trigger-1",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [240, 300]
    },
    {
      "parameters": {
        "url": "https://httpstat.us/500",
        "options": {}
      },
      "id": "http-1",
      "name": "HTTP Request (may fail)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [460, 300],
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "conditions": {
          "options": { "caseSensitive": true },
          "conditions": [
            {
              "leftValue": "={{ $json.error }}",
              "rightValue": "",
              "operator": { "type": "string", "operation": "exists" }
            }
          ]
        }
      },
      "id": "if-1",
      "name": "Error occurred?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [680, 300]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            { "id": "1", "name": "alert_message", "value": "={{ 'FAILED: ' + $json.error.message }}", "type": "string" },
            { "id": "2", "name": "failed_node", "value": "={{ $json.error.node?.name || 'unknown' }}", "type": "string" }
          ]
        }
      },
      "id": "set-1",
      "name": "Build alert",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [900, 200]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            { "id": "3", "name": "status", "value": "success", "type": "string" }
          ]
        }
      },
      "id": "set-2",
      "name": "Success path",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [900, 400]
    }
  ],
  "connections": {
    "Manual Trigger": { "main": [[{ "node": "HTTP Request (may fail)", "type": "main", "index": 0 }]] },
    "HTTP Request (may fail)": {
      "main": [[{ "node": "Error occurred?", "type": "main", "index": 0 }]],
      "error": [[{ "node": "Error occurred?", "type": "main", "index": 0 }]]
    },
    "Error occurred?": {
      "main": [
        [{ "node": "Build alert", "type": "main", "index": 0 }],
        [{ "node": "Success path", "type": "main", "index": 0 }]
      ]
    }
  },
  "pinData": {},
  "settings": { "executionOrder": "v1" }
}
Enter fullscreen mode Exit fullscreen mode

Quick reference: which pattern to use

Situation Pattern
Simple "alert me if anything breaks" Error Workflow (layer 2)
Skip bad items, process the rest Continue (ignoring error)
Retry once before alerting Continue (error output) + IF + Wait + retry
Route by HTTP status code Continue (error output) + Switch node
Log all failures to a sheet Continue (error output) + Google Sheets append

If you want more pre-built n8n workflows like this — covering webhooks, Stripe payments, Gmail automation, and more — I put together a Workflow Starter Pack with 10 ready-to-import JSONs ($29, one-time).

Drop any error handling questions in the comments — happy to help debug.

Top comments (1)

Collapse
 
pirateprentice profile image
Pirate Prentice

What's your go-to error handling pattern in n8n?

For me it's always: set the Error Workflow first (catches everything globally), then add Continue On Error to any node that hits an external API. The IF-node retry pattern for 429 rate limits has saved me more times than I can count.

Drop your setup in the comments - curious what people are doing for production workflows.