{
  "name": "Error workflow: classify, then alert twice",
  "nodes": [
    {
      "parameters": {},
      "id": "b1a0c1e2-0001-4a10-9c01-000000000001",
      "name": "Error Trigger",
      "type": "n8n-nodes-base.errorTrigger",
      "typeVersion": 1,
      "position": [-320, 0]
    },
    {
      "parameters": {
        "jsCode": "// n8n hands an error workflow one item shaped like this:\n//   { execution: { id, url, error, lastNodeExecuted, mode, retryOf }, workflow: { id, name } }\n// On a trigger-level failure `execution` can be absent, so read defensively.\n\nconst e = $json.execution || {};\nconst w = $json.workflow || {};\nconst err = e.error || {};\nconst message = String(err.message || 'No error message on the payload');\n\n// Pull an HTTP status out of the message when the node did not surface one.\nconst statusFromMessage = message.match(/\\b(4\\d{2}|5\\d{2})\\b/);\nconst status = Number(err.httpCode || err.statusCode || (statusFromMessage && statusFromMessage[1]) || 0);\n\nlet category = 'unknown';\nlet severity = 'warn';\nlet advice = 'Open the execution and read the last node that ran.';\n\nif (status === 401 || /invalid_grant|token|unauthori[sz]ed/i.test(message)) {\n  category = 'credentials';\n  severity = 'page';\n  advice = 'A token expired or was revoked. Reconnect the credential, then re-run.';\n} else if (status === 403) {\n  category = 'permissions';\n  severity = 'page';\n  advice = 'The account authenticated but is not allowed to do this. Check scopes and sharing.';\n} else if (status === 404 || status === 410) {\n  category = 'endpoint-gone';\n  severity = 'page';\n  advice = 'The URL or record no longer exists. The vendor may have retired this API version.';\n} else if (status === 429) {\n  category = 'rate-limit';\n  severity = 'warn';\n  advice = 'You are being throttled. Add a wait or batch the calls before retrying.';\n} else if (status >= 500) {\n  category = 'vendor-outage';\n  severity = 'warn';\n  advice = 'Their side. Retries usually clear it. If it persists past an hour, check their status page.';\n} else if (/ETIMEDOUT|ECONNRESET|ENOTFOUND|socket hang up/i.test(message)) {\n  category = 'network';\n  severity = 'warn';\n  advice = 'Connection never completed. Check the host and any egress firewall.';\n} else if (/Cannot read propert|undefined is not|is not a function/i.test(message)) {\n  category = 'shape-change';\n  severity = 'page';\n  advice = 'The data arriving is not the shape the code expects. Something upstream changed its response.';\n}\n\n// Dedupe key: same workflow, same node, same category. Use it downstream if you\n// add a store, so one broken integration does not send 200 identical messages.\nconst dedupeKey = [w.id || 'unknown', e.lastNodeExecuted || 'unknown', category].join(':');\n\nreturn [{\n  json: {\n    workflowName: w.name || 'Unnamed workflow',\n    workflowId: w.id || null,\n    executionId: e.id || null,\n    executionUrl: e.url || null,\n    failedNode: e.lastNodeExecuted || 'unknown',\n    mode: e.mode || 'unknown',\n    isRetry: Boolean(e.retryOf),\n    status: status || null,\n    category,\n    severity,\n    advice,\n    dedupeKey,\n    message: message.slice(0, 900),\n    stack: String(err.stack || '').slice(0, 1500)\n  }\n}];"
      },
      "id": "b1a0c1e2-0001-4a10-9c01-000000000002",
      "name": "Classify Failure",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [-100, 0]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://hooks.slack.com/services/REPLACE/WITH/YOUR_WEBHOOK",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ text: ($json.severity === 'page' ? ':rotating_light: ' : ':warning: ') + $json.workflowName + ' failed at ' + $json.failedNode, blocks: [ { type: 'section', text: { type: 'mrkdwn', text: '*' + $json.workflowName + '* failed at `' + $json.failedNode + '`\\n' + $json.message } }, { type: 'section', fields: [ { type: 'mrkdwn', text: '*Category*\\n' + $json.category }, { type: 'mrkdwn', text: '*HTTP*\\n' + ($json.status || 'n/a') }, { type: 'mrkdwn', text: '*Execution*\\n' + ($json.executionId || 'n/a') }, { type: 'mrkdwn', text: '*Retry*\\n' + ($json.isRetry ? 'yes' : 'no') } ] }, { type: 'context', elements: [ { type: 'mrkdwn', text: $json.advice + ($json.executionUrl ? ' <' + $json.executionUrl + '|Open execution>' : '') } ] } ] }) }}",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "id": "b1a0c1e2-0001-4a10-9c01-000000000003",
      "name": "Alert Slack",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [140, -110],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "sendTo": "REPLACE_WITH_YOUR_ADDRESS@example.com",
        "subject": "={{ ($json.severity === 'page' ? '[PAGE] ' : '[WARN] ') + $json.workflowName + ' failed at ' + $json.failedNode }}",
        "emailType": "text",
        "message": "={{ $json.workflowName + ' failed.\\n\\nNode: ' + $json.failedNode + '\\nCategory: ' + $json.category + '\\nHTTP status: ' + ($json.status || 'n/a') + '\\nExecution: ' + ($json.executionId || 'n/a') + '\\nMode: ' + $json.mode + '\\n\\nMessage:\\n' + $json.message + '\\n\\nWhat to do:\\n' + $json.advice + '\\n\\n' + ($json.executionUrl || '') }}",
        "options": {}
      },
      "id": "b1a0c1e2-0001-4a10-9c01-000000000004",
      "name": "Alert Email",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [140, 110],
      "onError": "continueRegularOutput"
    }
  ],
  "connections": {
    "Error Trigger": {
      "main": [[{ "node": "Classify Failure", "type": "main", "index": 0 }]]
    },
    "Classify Failure": {
      "main": [
        [
          { "node": "Alert Slack", "type": "main", "index": 0 },
          { "node": "Alert Email", "type": "main", "index": 0 }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "meta": {
    "instanceId": "shared-template"
  }
}
