AutomationFlowsAI & RAG › Error Handler (error Trigger)

Error Handler (error Trigger)

Error Handler. Uses errorTrigger, gmail. Event-driven trigger; 2 nodes.

Event trigger★☆☆☆☆ complexity2 nodesError TriggerGmail
AI & RAG Trigger: Event Nodes: 2 Complexity: ★☆☆☆☆ Added:

This workflow follows the Error Trigger → Gmail recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "nodes": [
    {
      "parameters": {
        "content": "```\nstatus         retry_count  error_type    severity  is_retryable  suggested_action   failed_node\n-------------------------------------------------------------------------------------------------------\npending_retry  0            unknown       medium    FALSE         manual_review      \npending_retry  0            unknown       medium    FALSE         manual_review      \npending_retry  0            unknown       medium    FALSE         manual_review      \npending_retry  0            unknown       medium    FALSE         manual_review      \npending_retry  0            parse_error   medium    FALSE         check_llm_output   \npending_retry  0            parse_error   medium    FALSE         check_llm_output   Accountant-concierge-LM\npending_retry  0            parse_error   medium    FALSE         check_llm_output   Accountant-concierge-LM\npending_retry  0            parse_error   medium    FALSE         check_llm_output   Accountant-concierge-LM\npending_retry  0            parse_error   medium    FALSE         check_llm_output   Call 'smart-table-fill'\npending_retry  0            parse_error   medium    FALSE         check_llm_output   Call 'smart-table-fill'\n```",
        "height": 256,
        "width": 1008
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -48,
        80
      ],
      "id": "31d79e15-243e-4416-a1eb-ef083978548f",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "content": "## Error Handler Workflow\n\n**Purpose:** Catch failed executions from all workflows, classify errors, log to Google Sheets, send Telegram alerts.\n\n**Setup:**\n1. Activate this workflow\n2. In n8n Settings \u2192 Error Workflow \u2192 Select this workflow\n3. Create `FailedItems` sheet tab with headers\n\n**CODE RED Alert:**\nIf the failing workflow is \"8-hour Task Resolver\", sends immediate CODE RED Telegram alert before normal processing. This catches the watchdog-failure scenario where the error handler itself is down.\n\n**Error Types:**\n- `auth_error` (401/403) \u2192 Critical, refresh credentials\n- `rate_limit` (429) \u2192 High, wait and retry\n- `network_error` \u2192 High, retry with backoff\n- `llm_schema_error` \u2192 High, auto-retry after 8h (retryable)\n- `parse_error` \u2192 Medium, check LLM output\n- `validation_error` \u2192 Medium, check input data\n- `unknown` \u2192 Medium, manual review\n\n**Long Message Support:**\nMessages over 4000 chars are split into chunks and sent as multiple Telegram messages with part numbers [1/3], [2/3], etc.\n\n\nuse a code-node like this to test:\n```\n// Test Error Handler - uncomment ONE at a time\n\n// Rate Limit (retryable)\nthrow new Error(\"429 Too Many Requests - rate limit exceeded\");     \n\n// Auth Error (critical)\n// throw new Error(\"401 Unauthorized - invalid credentials\");       \n\n// Network Error (retryable)\n// throw new Error(\"ETIMEDOUT - connection timed out\");\n\n// Parse Error\n// throw new Error(\"Unexpected token in JSON at position 0\");       \n\n// Validation Error\n// throw new Error(\"Required field 'email' is missing\");\n\n// Resource Error (critical)\n// throw new Error(\"Out of memory - cannot allocate buffer\");       \n\n// Unknown Error\n// throw new Error(\"Something weird happened\");\n```",
        "height": 944,
        "width": 480
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1088,
        144
      ],
      "id": "97da6e44-5c8f-4f09-b9a8-85dfd7b87899",
      "name": "Overview"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.errorTrigger",
      "typeVersion": 1,
      "position": [
        -544,
        592
      ],
      "id": "ac4bfaba-1429-46fb-a165-c58f6302ab7a",
      "name": "Error Trigger"
    },
    {
      "parameters": {
        "jsCode": "// Prepare Error Record\n// Extracts execution context from n8n error trigger\n// Also extracts attachment count for LLM cost estimation\n\nconst execution = $json.execution || {};\nconst workflow = $json.workflow || {};\nconst error = $json.error || {};\n\n// Extract failing node name from multiple possible locations\nconst failedNode = execution.lastNodeExecuted || \n                   error.node || \n                   error.nodeName || \n                   (error.context && error.context.nodeName) ||\n                   'unknown';\n\n// Extract error message from multiple possible locations\nlet errorMsg = error.message;\n\n// If no message, try alternative properties n8n might use\nif (!errorMsg) {\n  errorMsg = error.description || error.reason || error.cause ||\n             error.details || error.errorMessage;\n}\n\n// If still no message but error has a name that's descriptive, use it\nif (!errorMsg && error.name && error.name !== 'Error' && error.name !== 'NodeOperationError') {\n  errorMsg = error.name;\n}\n\n// Last resort: stringify, but check if it's actually useful\nif (!errorMsg) {\n  const stringified = JSON.stringify(error);\n  if (stringified && stringified !== '{}' && stringified !== 'null' && stringified.length > 2) {\n    errorMsg = stringified;\n  }\n}\n\n// Final fallback with specific hint\nif (!errorMsg) {\n  errorMsg = 'LLM output did not match required schema - check model response format';\n}\n\n// Extract retry_input - data needed to restart workflow from scratch\n// Searches execution runData for email_ID or other identifying info\nlet retryInputData = {};\nconst runData = execution.data?.resultData?.runData || {};\n\n// Search common nodes that hold identifying data\nconst nodesToCheck = ['Set File ID', 'Gmail', 'Gmail Trigger', 'Clean Email', 'Manual Trigger'];\nfor (const nodeName of nodesToCheck) {\n  const nodeData = runData[nodeName]?.[0]?.data?.main?.[0]?.[0]?.json;\n  if (nodeData) {\n    if (nodeData.email_ID) retryInputData.email_ID = nodeData.email_ID;\n    if (nodeData.id && !retryInputData.email_ID) retryInputData.email_ID = nodeData.id;\n    if (nodeData.messageId) retryInputData.messageId = nodeData.messageId;\n    break; // Found data, stop searching\n  }\n}\n\n// ====== Extract attachment count for LLM cost estimation ======\nlet attachmentCount = 0;\n\n// Strategy 1: Check Gmail node for binary attachments\nconst gmailNodeData = runData['Gmail']?.[0]?.data?.main?.[0]?.[0];\nif (gmailNodeData?.binary) {\n  attachmentCount = Object.keys(gmailNodeData.binary).length;\n}\n\n// Strategy 2: Check Gmail Trigger node\nif (attachmentCount === 0) {\n  const gmailTriggerData = runData['Gmail Trigger']?.[0]?.data?.main?.[0]?.[0];\n  if (gmailTriggerData?.binary) {\n    attachmentCount = Object.keys(gmailTriggerData.binary).length;\n  }\n}\n\n// Strategy 3: Check \"sp\" (Split Out) node output count\nif (attachmentCount === 0) {\n  const spNodeData = runData['sp'];\n  if (spNodeData && spNodeData[0]?.data?.main?.[0]) {\n    attachmentCount = spNodeData[0].data.main[0].length;\n  }\n}\n\n// Strategy 4: Check Clean Email object for attachmentNames\nif (attachmentCount === 0) {\n  const cleanEmailData = runData['Clean Email object']?.[0]?.data?.main?.[0]?.[0]?.json;\n  if (cleanEmailData?.data?.[0]?.attachmentNames) {\n    attachmentCount = cleanEmailData.data[0].attachmentNames.length;\n  }\n}\n\n// Default to 1 if we couldn't determine (conservative estimate)\nif (attachmentCount === 0) {\n  attachmentCount = 1;\n}\n\nreturn [{\n  json: {\n    timestamp: new Date().toISOString(),\n    workflow_id: workflow.id || 'unknown',\n    workflow_name: workflow.name || 'unknown',\n    execution_id: execution.retryOf || execution.id || 'unknown',\n    execution_mode: execution.mode || 'unknown',\n    failed_node: failedNode,\n    retry_of: execution.retryOf || '',\n    error_message: errorMsg.substring(0, 500),\n    error_stack: (error.stack || '').substring(0, 1000),\n    error_name: error.name || 'Error',\n    started_at: execution.startedAt || '',\n    status: 'pending_retry',\n    retry_count: 0,\n    retry_input: JSON.stringify(retryInputData),\n    attachment_count: attachmentCount\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -320,
        592
      ],
      "id": "c4ae7909-8ba0-413b-babf-efff2a799d07",
      "name": "Prepare Error Record"
    },
    {
      "parameters": {
        "jsCode": "// Classify Error by Type\n// Determines severity, retryability, suggested action\n// Also calculates expected LLM requests for rate-limit-aware retries\n\nconst errorMsg = ($json.error_message || '').toLowerCase();\nconst errorMsgOriginal = $json.error_message || '';\nconst errorStack = ($json.error_stack || '').toLowerCase();\nconst errorName = ($json.error_name || '').toLowerCase();\nconst failedNode = $json.failed_node || 'unknown';\nconst attachmentCount = $json.attachment_count || 1;\nconst workflowName = $json.workflow_name || '';\n\n// ====== Auto-Retry Registry ======\n// Workflows that support automatic retry on rate limits\nconst AUTO_RETRY_REGISTRY = [\n  'smart-folder2table',\n  'any-file2json-converter',\n  // Add more as needed\n];\n\nconst canAutoRetry = AUTO_RETRY_REGISTRY.some(name =>\n  workflowName.toLowerCase().includes(name.toLowerCase())\n);\n\nlet errorType = 'unknown';\nlet severity = 'medium';\nlet isRetryable = false;\nlet suggestedAction = 'manual_review';\n\n// Auth errors (401, 403, credential issues)\nif (errorMsg.includes('401') || errorMsg.includes('403') ||\n    errorMsg.includes('unauthorized') || errorMsg.includes('forbidden') ||\n    errorMsg.includes('credential') || errorMsg.includes('token expired') ||\n    errorMsg.includes('invalid_grant') || errorMsg.includes('authentication')) {\n  errorType = 'auth_error';\n  severity = 'critical';\n  suggestedAction = 'refresh_credentials';\n  isRetryable = false;\n}\n// Rate limits (429)\nelse if (errorMsg.includes('429') || errorMsg.includes('rate limit') ||\n         errorMsg.includes('too many requests') || errorMsg.includes('quota') ||\n         errorMsg.includes('throttl')) {\n  errorType = 'rate_limit';\n  severity = 'high';\n  suggestedAction = 'wait_and_retry';\n  isRetryable = true;\n}\n// Network/timeout errors\nelse if (errorMsg.includes('timeout') || errorMsg.includes('etimedout') ||\n         errorMsg.includes('econnrefused') || errorMsg.includes('econnreset') ||\n         errorMsg.includes('network') || errorMsg.includes('enotfound') ||\n         errorMsg.includes('socket hang up') || errorMsg.includes('502') ||\n         errorMsg.includes('503') || errorMsg.includes('504')) {\n  errorType = 'network_error';\n  severity = 'high';\n  suggestedAction = 'retry_with_backoff';\n  isRetryable = true;\n}\n// LLM Schema errors (specific pattern - retryable because LLMs are non-deterministic)\nelse if (errorMsg.includes('llm output did not match required schema')) {\n  errorType = 'llm_schema_error';\n  severity = 'high';\n  suggestedAction = 'auto_retry_8h';\n  isRetryable = true;\n}\n// Parse errors (JSON, schema) - general case, NOT retryable\nelse if (errorMsg.includes('json') || errorMsg.includes('parse') ||\n         errorMsg.includes('unexpected token') || errorMsg.includes('schema') ||\n         errorMsg.includes('syntax error') || errorName.includes('syntaxerror')) {\n  errorType = 'parse_error';\n  severity = 'medium';\n  suggestedAction = 'check_llm_output';\n  isRetryable = false;\n}\n// Validation errors\nelse if (errorMsg.includes('required') || errorMsg.includes('missing') ||\n         errorMsg.includes('invalid') || errorMsg.includes('validation') ||\n         errorMsg.includes('not found') || errorMsg.includes('does not exist')) {\n  errorType = 'validation_error';\n  severity = 'medium';\n  suggestedAction = 'check_input_data';\n  isRetryable = false;\n}\n// Resource errors\nelse if (errorMsg.includes('memory') || errorMsg.includes('disk') ||\n         errorMsg.includes('storage') || errorMsg.includes('out of space')) {\n  errorType = 'resource_error';\n  severity = 'critical';\n  suggestedAction = 'check_resources';\n  isRetryable = false;\n}\n\n// ====== LLM Cost Estimation by Provider ======\n// Rate limits are per API provider, so we track them separately:\n// - Groq: 5 req/min (bottleneck causing 429 errors)\n// - Google Gemini: Separate limit for OCR (not typically the bottleneck)\nconst LLM_COST_BY_NODE = {\n  // Early failures - most work remaining\n  'Gmail Trigger':           { groq_base: 1, groq_per_att: 1, google_per_att: 1 },\n  'Clean Email object':      { groq_base: 1, groq_per_att: 1, google_per_att: 1 },\n  \n  // Classification stage (Groq)\n  'subject-classifier-LM':   { groq_base: 1, groq_per_att: 1, google_per_att: 0 },\n  \n  // Attachment processing stage\n  'any-file2json-converter': { groq_base: 0, groq_per_att: 1, google_per_att: 1 },\n  'Create Attachment Profile':{ groq_base: 0, groq_per_att: 1, google_per_att: 1 },\n  \n  // Invoice extraction stage (Groq only)\n  'Accountant-concierge-LM': { groq_base: 0, groq_per_att: 1, google_per_att: 0 },\n  \n  // smart-table-fill (Groq, batched) - 4 batches typical\n  'Extract Data from String':{ groq_base: 0, groq_per_att: 4, google_per_att: 0 },\n  \"Call 'smart-table-fill'\": { groq_base: 0, groq_per_att: 4, google_per_att: 0 },\n  'smart-table-fill':        { groq_base: 0, groq_per_att: 4, google_per_att: 0 },\n  \n  // Default for unknown nodes\n  '_default':                { groq_base: 1, groq_per_att: 1, google_per_att: 1 }\n};\n\nconst cost = LLM_COST_BY_NODE[failedNode] || LLM_COST_BY_NODE['_default'];\nconst expected_groq_requests = cost.groq_base + (attachmentCount * cost.groq_per_att);\nconst expected_google_requests = attachmentCount * cost.google_per_att;\n\n// Package LLM cost estimate as single JSON field\n// Different workflows have different cost structures, so keep as flexible package\nconst llmCostEstimate = {\n  attachment_count: attachmentCount,\n  groq: expected_groq_requests,\n  google: expected_google_requests\n};\n\n// ====== Extract Retry Timing for Rate Limits ======\nlet retryAfterSeconds = 60; // default\nif (errorType === 'rate_limit') {\n  // Pattern: \"retry in 55.251598518s\" or \"retry in 55 seconds\"\n  const retryMatch = errorMsgOriginal.match(/retry in (\\d+(?:\\.\\d+)?)\\s*s/i);\n  if (retryMatch) {\n    retryAfterSeconds = Math.ceil(parseFloat(retryMatch[1]));\n  }\n}\n\nreturn [{\n  json: {\n    ...($json),\n    error_type: errorType,\n    severity: severity,\n    is_retryable: isRetryable,\n    suggested_action: suggestedAction,\n    retry_params: JSON.stringify(llmCostEstimate),\n    can_auto_retry: canAutoRetry,\n    retry_after_seconds: retryAfterSeconds\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -96,
        592
      ],
      "id": "fc3a9fa9-042f-4b1c-a01e-6dad08795d7e",
      "name": "Classify Error"
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "YOUR_FAILEDITEMS_SHEET_ID",
          "mode": "list",
          "cachedResultName": "007_Error-handler.n8n-sheet",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_FAILEDITEMS_SHEET_ID/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "FailedItems",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/YOUR_FAILEDITEMS_SHEET_ID/edit#gid=0"
        },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {},
          "matchingColumns": [
            "execution_id"
          ],
          "schema": [
            {
              "id": "timestamp",
              "displayName": "timestamp",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "workflow_id",
              "displayName": "workflow_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "workflow_name",
              "displayName": "workflow_name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "execution_id",
              "displayName": "execution_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "execution_mode",
              "displayName": "execution_mode",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "failed_node",
              "displayName": "failed_node",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "retry_of",
              "displayName": "retry_of",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "error_message",
              "displayName": "error_message",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "error_stack",
              "displayName": "error_stack",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "error_name",
              "displayName": "error_name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "started_at",
              "displayName": "started_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "retry_count",
              "displayName": "retry_count",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "error_type",
              "displayName": "error_type",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "severity",
              "displayName": "severity",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "is_retryable",
              "displayName": "is_retryable",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "suggested_action",
              "displayName": "suggested_action",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "retry_input",
              "displayName": "retry_input",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": false
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [
        128,
        592
      ],
      "id": "44879a1e-9691-43e0-978f-2ed0026a8572",
      "name": "Append to FailedItems",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Format Telegram Alert with Severity Emoji\n// Outputs FULL message (no truncation) - chunking handled downstream\n// Includes LLM cost estimates for rate-limit awareness\n\nconst esc = (s) => String(s).replace(/_/g, '\\\\_');\n\nconst severityEmoji = {\n  critical: '\\uD83D\\uDD34', // Red circle\n  high: '\\uD83D\\uDFE0',     // Orange circle\n  medium: '\\uD83D\\uDFE1',   // Yellow circle\n  low: '\\uD83D\\uDFE2'       // Green circle\n};\n\nconst emoji = severityEmoji[$json.severity] || '\\u26AA'; // White circle default\n\n// Parse LLM cost estimate JSON\nconst llmCost = JSON.parse($json.retry_params || '{}');\n\n// Build full message with failed node prominently displayed\n// Added attachment count and LLM estimates for rate limit visibility\nconst msg = `${emoji} ${esc($json.error_type).toUpperCase()}\n\\u274C Workflow: ${esc($json.workflow_name)}\n\\uD83D\\uDCCD Node: ${esc($json.failed_node || 'unknown')}\n---\nError: ${esc($json.error_message)}\n---\nExecution: ${$json.execution_id}\nSeverity: ${$json.severity}\nRetryable: ${$json.is_retryable ? 'Yes' : 'No'}\nAction: ${esc($json.suggested_action)}\nAttachments: ${llmCost.attachment_count || 0}\nLLM calls: Groq=${llmCost.groq || 0} | Google=${llmCost.google || 0}\nStack: ${esc($json.error_stack || 'N/A')}`;\n\nreturn [{\n  json: {\n    chatId: 'YOUR_TELEGRAM_CHAT_ID',\n    fullMessage: msg\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        352,
        592
      ],
      "id": "e7f5d957-8e4d-49d7-a9a0-d00fc403e7f0",
      "name": "Format Telegram Alert"
    },
    {
      "parameters": {
        "jsCode": "// Split Long Message into Chunks\n// Telegram max message length is ~4096 chars, using 4000 to be safe\n\nconst message = $json.fullMessage || 'No error message';\nconst chatId = $json.chatId;\nconst maxLength = 4000;\nconst chunks = [];\n\nfor (let i = 0; i < message.length; i += maxLength) {\n  chunks.push(message.substring(i, i + maxLength));\n}\n\nreturn chunks.map((chunk, idx) => ({\n  json: {\n    chatId: chatId,\n    chunk: chunk,\n    part: idx + 1,\n    total: chunks.length\n  }\n}));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        576,
        592
      ],
      "id": "fbc7df72-a695-42f1-b1a5-29f9055b93fd",
      "name": "Split Long Message"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        800,
        592
      ],
      "id": "ff1ddec4-fe63-4059-ba4f-b8fb2b711ef0",
      "name": "Loop Over Chunks"
    },
    {
      "parameters": {
        "chatId": "=YOUR_CHAT_ID_1",
        "text": "={{ $json.total > 1 ? '[' + $json.part + '/' + $json.total + '] ' : '' }}{{ $json.chunk }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1024,
        592
      ],
      "id": "cf9f8582-c986-49e3-9ddc-d35bbb196100",
      "name": "Send Telegram Alert",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "resolver-failure-check",
              "leftValue": "={{ $json.workflow_name }}",
              "rightValue": "8-hour Task Resolver",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        192,
        976
      ],
      "id": "300a706d-0ff2-4945-ae32-b1b0efcbd59e",
      "name": "Is Resolver Failure?"
    },
    {
      "parameters": {
        "chatId": "YOUR_CHAT_ID_1",
        "text": "=\ud83d\udea8\ud83d\udea8\ud83d\udea8 CODE RED \ud83d\udea8\ud83d\udea8\ud83d\udea8\n\n\u26a0\ufe0f ERROR HANDLER DOWN \u26a0\ufe0f\n\nThe 8-hour Task Resolver has FAILED!\nThis means automatic error recovery is broken.\n\nFailed Node: {{ $json.failed_node }}\nError: {{ $json.error_message }}\nExecution: {{ $json.execution_id }}\n\n\ud83d\udd25 IMMEDIATE ATTENTION REQUIRED \ud83d\udd25",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        192,
        784
      ],
      "id": "ac4c8411-3c5f-4d31-bd8e-6d108fe297f3",
      "name": "CODE RED Alert",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "rate-limit-check",
              "leftValue": "={{ $json.error_type }}",
              "rightValue": "rate_limit",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "auto-retry-check",
              "leftValue": "={{ $json.can_auto_retry }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -96,
        976
      ],
      "id": "eh-if-rate-limit-auto-retry",
      "name": "IF: Rate Limit Auto-Retry?"
    },
    {
      "parameters": {
        "jsCode": "// Extract Config for Execute Workflow retry\n// Gets original Config values from failed execution's runData\nconst execution = $('Error Trigger').first().json.execution || {};\nconst runData = execution.data?.resultData?.runData || {};\n\n// Extract Config node output from smart-folder2table\nconst configData = runData['Config']?.[0]?.data?.main?.[0]?.[0]?.json || {};\n\nreturn [{\n  json: {\n    // Pass original config values\n    folder_id: configData.folder_id || '',\n    spreadsheet_id: configData.spreadsheet_id || '',\n    data_sheet_name: configData.data_sheet_name || 'Sheet1',\n    source_file_column: configData.source_file_column || 'source_file',\n    match_column: configData.match_column || 'source_file',\n    batch_size: configData.batch_size || 10,\n    schema_sheet_name: configData.schema_sheet_name || 'Description_hig7f6',\n    // Add new rate limit param from error classification\n    rate_limit_wait_seconds: $('Classify Error').first().json.retry_after_seconds || 60\n  }\n}];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        128,
        1168
      ],
      "id": "eh-extract-config-for-retry",
      "name": "Extract Config for Retry"
    },
    {
      "parameters": {
        "amount": "={{ $('Classify Error').first().json.retry_after_seconds }}",
        "unit": "seconds"
      },
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        352,
        1168
      ],
      "id": "eh-wait-rate-limit",
      "name": "Wait (Rate Limit)"
    },
    {
      "parameters": {
        "workflowId": {
          "__rl": true,
          "value": "={{ $('Classify Error').first().json.workflow_id }}",
          "mode": "id"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "folder_id": "={{ $json.folder_id }}",
            "spreadsheet_id": "={{ $json.spreadsheet_id }}",
            "data_sheet_name": "={{ $json.data_sheet_name }}",
            "source_file_column": "={{ $json.source_file_column }}",
            "match_column": "={{ $json.match_column }}",
            "batch_size": "={{ $json.batch_size }}",
            "schema_sheet_name": "={{ $json.schema_sheet_name }}",
            "rate_limit_wait_seconds": "={{ $json.rate_limit_wait_seconds }}"
          },
          "matchingColumns": [],
          "schema": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "waitForSubWorkflow": true
        }
      },
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.2,
      "position": [
        576,
        1168
      ],
      "id": "eh-execute-smart-folder2table",
      "name": "Retry Workflow (Execute)"
    },
    {
      "parameters": {
        "chatId": "YOUR_CHAT_ID_1",
        "text": "=\u23f3 Rate limit hit in {{ $('Classify Error').first().json.workflow_name }}\n\nRestarting via Execute Workflow after {{ $('Classify Error').first().json.retry_after_seconds }}s delay...\n\nFailed node: {{ $('Classify Error').first().json.failed_node }}\nrate_limit_wait_seconds={{ $('Classify Error').first().json.retry_after_seconds }}\n\nResumability: Already-processed files will be skipped.",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        800,
        1168
      ],
      "id": "eh-auto-retry-alert",
      "name": "Send Auto-Retry Alert",
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "content": "## Auto-Retry for Rate Limits\n\n**Registry-based**: Only workflows in AUTO_RETRY_REGISTRY get immediate retry.\nOthers go through normal flow \u2192 8-hour resolver.\n\n**Current registry:**\n- smart-folder2table\n- any-file2json-converter\n\n**Timing:** Extracts \"retry in Xs\" from error message, defaults to 60s.\n\n**How it works:**\n1. Extracts original Config values from failed execution's runData\n2. Waits for rate limit period\n3. Calls the SAME workflow that failed (by ID from error data):\n   - Original config (folder_id, spreadsheet_id, etc.)\n   - rate_limit_wait_seconds = extracted retry timing\n\n**Workflow-agnostic:** Uses dynamic workflow_id from Classify Error,\nso any workflow in the registry can be auto-retried.\n\n**Resumability:**\nWorkflows check sheet for already-processed files.\nAfter restart, completed items are skipped.",
        "height": 480,
        "width": 380
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -280,
        1080
      ],
      "id": "eh-sticky-auto-retry",
      "name": "Sticky Note - Auto-Retry"
    },
    {
      "parameters": {
        "content": "### If stops here\n\nSet document \u2192 your FailedItems spreadsheet,\nsheet \u2192 `FailedItems`,\nmatch column \u2192 `execution_id`",
        "height": 80,
        "width": 150
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        100,
        560
      ],
      "id": "eh-sticky-faileditems-setup",
      "name": "Sticky Note - FailedItems Setup"
    }
  ],
  "connections": {
    "Error Trigger": {
      "main": [
        [
          {
            "node": "Prepare Error Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Error Record": {
      "main": [
        [
          {
            "node": "Classify Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Error": {
      "main": [
        [
          {
            "node": "IF: Rate Limit Auto-Retry?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF: Rate Limit Auto-Retry?": {
      "main": [
        [
          {
            "node": "Extract Config for Retry",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Is Resolver Failure?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Config for Retry": {
      "main": [
        [
          {
            "node": "Wait (Rate Limit)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait (Rate Limit)": {
      "main": [
        [
          {
            "node": "Retry Workflow (Execute)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retry Workflow (Execute)": {
      "main": [
        [
          {
            "node": "Send Auto-Retry Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append to FailedItems": {
      "main": [
        [
          {
            "node": "Format Telegram Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Telegram Alert": {
      "main": [
        [
          {
            "node": "Split Long Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Long Message": {
      "main": [
        [
          {
            "node": "Loop Over Chunks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Chunks": {
      "main": [
        [],
        [
          {
            "node": "Send Telegram Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Telegram Alert": {
      "main": [
        [
          {
            "node": "Loop Over Chunks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Resolver Failure?": {
      "main": [
        [
          {
            "node": "CODE RED Alert",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Append to FailedItems",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sticky Note - FailedItems Setup": {}
  },
  "meta": {
    "templateCredsSetupCompleted": true
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Error Handler. Uses errorTrigger, gmail. Event-driven trigger; 2 nodes.

Source: https://github.com/alibhatti59/ai-lead-qualification-agent/blob/master/error-handler-workflow.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

This workflow is designed for Scrum Masters and Agile Coaches who prepare and coordinate Sprint Planning sessions, using Google Calendar, Google Sheets, and OpenAI.

Google Calendar, Google Sheets, Agent +4
AI & RAG

This workflow is designed for Scrum Masters, Agile Coaches, and Product Owners who want to automate backlog refinement preparation using Google Sheets, Gmail, and OpenAI. It’s ideal for teams seeking

Google Calendar, Google Sheets, Agent +4
AI & RAG

This workflow implements an AI-powered WhatsApp booking assistant for a hair salon. The system allows customers to book, reschedule, or cancel appointments automatically via text or voice messages on

Google Calendar Tool, Google Sheets Tool, Error Trigger +15
AI & RAG

Submit a research topic through a form and receive a professionally styled executive report in your inbox — fully automated, with built-in scraping resilience.

Form Trigger, HTTP Request, Chain Llm +3
AI & RAG

Transform your manual hiring process into an intelligent evaluation system that saves 15-20 minutes per candidate! This workflow automates the entire candidate assessment pipeline - from CSV/XLSX uplo

Form Trigger, Google Sheets, Google Drive +8