Skip to main content

Error Handling Workflow Example

This example demonstrates comprehensive error handling patterns including try-catch logic, fallback mechanisms, error notifications, and graceful degradation. Essential for production workflows.

Workflow Overview

The workflow implements robust error handling:
  1. Webhook Trigger - Initiates workflow
  2. Try-Catch Pattern - HTTP request with error handling
  3. Error Output - Dedicated error processing branch
  4. Fallback Logic - Alternative data sources
  5. Notification - Alert on failures
  6. Response - Appropriate status codes and messages

Use Cases

  • Production API integrations
  • Mission-critical workflows
  • Third-party service integration
  • Data pipeline resilience
  • User-facing automation

Complete Workflow JSON

{
  "name": "Production Error Handling",
  "nodes": [
    {
      "id": "webhook-1",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [250, 450],
      "parameters": {
        "httpMethod": "POST",
        "path": "fetch-data",
        "responseMode": "lastNode",
        "options": {}
      },
      "webhookId": "error-handling-demo"
    },
    {
      "id": "http-primary-1",
      "name": "Primary API Call",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [450, 350],
      "parameters": {
        "method": "GET",
        "url": "https://api.primary-service.com/data/{{$json.id}}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Request-ID",
              "value": "={{$workflow.id}}-{{$runIndex}}-{{$now.toUnixInteger()}}"
            },
            {
              "name": "X-Retry-Count",
              "value": "0"
            }
          ]
        },
        "options": {
          "timeout": 10000,
          "retry": {
            "maxRetries": 3,
            "retryInterval": 1000
          },
          "response": {
            "response": {
              "fullResponse": true
            }
          }
        }
      },
      "continueOnFail": true,
      "onError": "continueErrorOutput",
      "credentials": {
        "httpHeaderAuth": {
          "id": "1",
          "name": "API Auth"
        }
      }
    },
    {
      "id": "if-success-1",
      "name": "Check Success",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [650, 350],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-1",
              "leftValue": "={{$json.statusCode}}",
              "rightValue": "200",
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "set-success-1",
      "name": "Format Success Response",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [850, 250],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "assign-1",
              "name": "status",
              "value": "success",
              "type": "string"
            },
            {
              "id": "assign-2",
              "name": "data",
              "value": "={{$json.body}}",
              "type": "object"
            },
            {
              "id": "assign-3",
              "name": "source",
              "value": "primary",
              "type": "string"
            },
            {
              "id": "assign-4",
              "name": "timestamp",
              "value": "={{$now}}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "http-fallback-1",
      "name": "Fallback API Call",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [850, 450],
      "parameters": {
        "method": "GET",
        "url": "https://api.backup-service.com/data/{{$('Webhook Trigger').item.json.id}}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "timeout": 10000
        }
      },
      "continueOnFail": true,
      "credentials": {
        "httpHeaderAuth": {
          "id": "2",
          "name": "Backup API Auth"
        }
      }
    },
    {
      "id": "set-fallback-1",
      "name": "Format Fallback Response",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [1050, 450],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "assign-1",
              "name": "status",
              "value": "success_fallback",
              "type": "string"
            },
            {
              "id": "assign-2",
              "name": "data",
              "value": "={{$json}}",
              "type": "object"
            },
            {
              "id": "assign-3",
              "name": "source",
              "value": "fallback",
              "type": "string"
            },
            {
              "id": "assign-4",
              "name": "message",
              "value": "Primary service unavailable, using fallback",
              "type": "string"
            },
            {
              "id": "assign-5",
              "name": "timestamp",
              "value": "={{$now}}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "set-error-1",
      "name": "Format Error Response",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [850, 650],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "assign-1",
              "name": "status",
              "value": "error",
              "type": "string"
            },
            {
              "id": "assign-2",
              "name": "error",
              "value": "={{{
  message: $json.message || 'Unknown error occurred',
  statusCode: $json.statusCode || 500,
  errorType: $json.error?.type || 'UNKNOWN_ERROR',
  details: $json.error
}}}",
              "type": "object"
            },
            {
              "id": "assign-3",
              "name": "timestamp",
              "value": "={{$now}}",
              "type": "string"
            },
            {
              "id": "assign-4",
              "name": "request_id",
              "value": "={{$workflow.id}}-{{$runIndex}}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "slack-1",
      "name": "Alert Team",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.2,
      "position": [1050, 650],
      "parameters": {
        "resource": "message",
        "operation": "post",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "C0123456789",
          "mode": "id"
        },
        "messageType": "block",
        "blocksUi": {
          "blocksValues": [
            {
              "type": "section",
              "text": {
                "type": "mrkdwn",
                "text": "*🚨 Workflow Error Alert*\n\n*Workflow:* {{$workflow.name}}\n*Error:* {{$json.error.message}}\n*Request ID:* {{$json.request_id}}\n*Timestamp:* {{$json.timestamp}}"
              }
            }
          ]
        },
        "otherOptions": {}
      },
      "continueOnFail": true,
      "credentials": {
        "slackApi": {
          "id": "3",
          "name": "Slack Notifications"
        }
      }
    },
    {
      "id": "merge-1",
      "name": "Merge All Outcomes",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [1250, 450],
      "parameters": {
        "mode": "append",
        "options": {}
      }
    },
    {
      "id": "respond-1",
      "name": "Send Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [1450, 450],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{$json}}",
        "options": {
          "responseCode": "={{$json.status === 'error' ? 500 : ($json.status === 'success_fallback' ? 206 : 200)}}"
        }
      }
    }
  ],
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Primary API Call",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Primary API Call": {
      "main": [
        [
          {
            "node": "Check Success",
            "type": "main",
            "index": 0
          }
        ]
      ],
      "error": [
        [
          {
            "node": "Format Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Success": {
      "main": [
        [
          {
            "node": "Format Success Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fallback API Call",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Success Response": {
      "main": [
        [
          {
            "node": "Merge All Outcomes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fallback API Call": {
      "main": [
        [
          {
            "node": "Format Fallback Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Fallback Response": {
      "main": [
        [
          {
            "node": "Merge All Outcomes",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Format Error Response": {
      "main": [
        [
          {
            "node": "Alert Team",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert Team": {
      "main": [
        [
          {
            "node": "Merge All Outcomes",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Merge All Outcomes": {
      "main": [
        [
          {
            "node": "Send Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "saveExecutionProgress": true
  },
  "active": false
}

Error Handling Architecture

Webhook → Primary API (with retry)
            |
            |-- Success (200) → Format Success → Merge → Respond
            |
            |-- Partial Fail (non-200) → Fallback API → Format Fallback → Merge → Respond
            |
            |-- Error Output → Format Error → Alert Team → Merge → Respond

Step-by-Step Breakdown

1

Primary API with Error Handling

The HTTP node is configured for resilience:Key Settings:
  • continueOnFail: true - Workflow continues on errors
  • onError: "continueErrorOutput" - Routes errors to error output
  • retry.maxRetries: 3 - Automatic retry with backoff
  • fullResponse: true - Access status codes
2

Success Path

When the API returns 200:
  1. IF node checks status code
  2. Formats success response
  3. Includes source metadata
  4. Returns HTTP 200
3

Fallback Path

When API returns non-200 but doesn’t error:
  1. IF node routes to FALSE branch
  2. Calls fallback API
  3. Formats with “success_fallback” status
  4. Returns HTTP 206 (Partial Content)
4

Error Path

When request fails completely:
  1. Error output activates
  2. Formats detailed error response
  3. Sends Slack notification to team
  4. Returns HTTP 500 with error details
5

Response Handling

All three paths merge and return:
  • Success: HTTP 200
  • Fallback: HTTP 206
  • Error: HTTP 500
Client always receives a response!

Response Examples

Success Response (HTTP 200)

{
  "status": "success",
  "data": {
    "id": "valid-123",
    "name": "Example Data",
    "value": 42
  },
  "source": "primary",
  "timestamp": "2024-03-15T10:30:00.000Z"
}

Fallback Response (HTTP 206)

{
  "status": "success_fallback",
  "data": {
    "id": "fallback-456",
    "name": "Cached Data",
    "value": 35
  },
  "source": "fallback",
  "message": "Primary service unavailable, using fallback",
  "timestamp": "2024-03-15T10:30:05.000Z"
}

Error Response (HTTP 500)

{
  "status": "error",
  "error": {
    "message": "Connection timeout",
    "statusCode": 500,
    "errorType": "TIMEOUT_ERROR",
    "details": {
      "code": "ETIMEDOUT",
      "host": "api.primary-service.com"
    }
  },
  "timestamp": "2024-03-15T10:30:10.000Z",
  "request_id": "wf-12345-0"
}

Key Error Handling Patterns

1. Continue On Fail

Critical Setting: Always set continueOnFail: true on nodes where errors are expected. This prevents the entire workflow from failing.
{
  "continueOnFail": true,
  "onError": "continueErrorOutput"
}

2. Error Output Routing

Use dedicated error connections:
{
  "connections": {
    "API Call": {
      "main": [[{"node": "Success Handler"}]],
      "error": [[{"node": "Error Handler"}]]
    }
  }
}

3. Retry Logic

{
  "options": {
    "retry": {
      "maxRetries": 3,
      "retryInterval": 1000  // ms between retries
    }
  }
}

4. Graceful Degradation

Always provide fallback:
  • Alternative API endpoints
  • Cached data
  • Default values
  • User-friendly error messages

Advanced Patterns

Circuit Breaker Pattern

Add rate limiting after repeated failures:
{
  "name": "Circuit Breaker Check",
  "type": "n8n-nodes-base.if",
  "parameters": {
    "conditions": {
      "conditions": [
        {
          "leftValue": "={{$('Get Failure Count').item.json.count}}",
          "operator": "smaller",
          "rightValue": "5"
        }
      ]
    }
  }
}

Dead Letter Queue

Store failed items for manual review:
{
  "name": "Store Failed Items",
  "type": "n8n-nodes-base.postgres",
  "parameters": {
    "operation": "insert",
    "table": "failed_requests",
    "columns": "request_data, error_message, timestamp"
  }
}

Exponential Backoff

Implement custom retry logic:
{
  "name": "Calculate Backoff",
  "type": "n8n-nodes-base.code",
  "parameters": {
    "jsCode": "const retryCount = $input.all()[0].json.retryCount || 0;\nconst backoff = Math.min(1000 * Math.pow(2, retryCount), 30000);\nreturn { backoff, retryCount: retryCount + 1 };"
  }
}

Production Checklist

1

Node Configuration

  • continueOnFail: true on all external calls
  • onError: continueErrorOutput enabled
  • Retry logic configured (3 retries minimum)
  • Timeouts set appropriately (10s recommended)
2

Workflow Settings

  • saveDataErrorExecution: "all" for debugging
  • saveExecutionProgress: true for monitoring
  • Execution timeout configured
  • Error workflow ID set (optional)
3

Monitoring

  • Error notifications configured (Slack/email)
  • Logging to external service
  • Request ID tracking
  • Execution history retention
4

Testing

  • Test success path
  • Test fallback path
  • Test complete failure
  • Test retry mechanism
  • Test notification delivery

Monitoring and Alerting

Slack Notification Best Practices

{
  "messageType": "block",
  "blocksUi": {
    "blocksValues": [
      {
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": "*🚨 Alert*\n*Workflow:* {{$workflow.name}}\n*Error:* {{$json.error.message}}\n*ID:* {{$json.request_id}}"
        }
      },
      {
        "type": "actions",
        "elements": [
          {
            "type": "button",
            "text": "View Execution",
            "url": "https://n8n.example.com/workflow/{{$workflow.id}}/executions"
          }
        ]
      }
    ]
  }
}

Error Tracking Service

Integrate with Sentry, Datadog, or similar:
{
  "name": "Log to Sentry",
  "type": "n8n-nodes-base.httpRequest",
  "parameters": {
    "method": "POST",
    "url": "https://sentry.io/api/0/projects/YOUR_PROJECT/events/",
    "sendBody": true,
    "jsonBody": "={{JSON.stringify({\n  message: $json.error.message,\n  level: 'error',\n  tags: {\n    workflow: $workflow.name,\n    request_id: $json.request_id\n  }\n})}}"
  }
}

Next Steps

Build docs developers (and LLMs) love