AutomationFlowsAI & RAG › Classify Workflow Errors with Claude and Deduplicate Jira and Slack Alerts

Classify Workflow Errors with Claude and Deduplicate Jira and Slack Alerts

ByMychel Garzon @mychel-garzon on n8n.io

This global error-handling workflow classifies n8n execution failures with Anthropic Claude, deduplicates incidents in Jira using a hash label, and posts a detailed alert to Slack with links to the affected execution and Jira issue. Triggers whenever any n8n workflow execution…

Event trigger★★★★☆ complexityAI-powered24 nodesError TriggerChain LlmAnthropic ChatCryptoJiraSlack
AI & RAG Trigger: Event Nodes: 24 Complexity: ★★★★☆ AI nodes: yes Added:
Classify Workflow Errors with Claude and Deduplicate Jira and Slack Alerts — n8n workflow card showing Error Trigger, Chain Llm, Anthropic Chat integration

This workflow corresponds to n8n.io template #16646 — we link there as the canonical source.

This workflow follows the Chainllm → Anthropic Chat 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
{
  "id": "0RNUOExtUZ055C4K",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Classify workflow errors with Claude and deduplicate Jira and Slack alerts",
  "tags": [],
  "nodes": [
    {
      "id": "7b82665e-e887-419d-afe5-4f000b69c87c",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3008,
        -896
      ],
      "parameters": {
        "width": 480,
        "height": 1168,
        "content": "## AI Error Observability \u2014 Claude Classification + Jira Deduplication\n\n### How it works\n\nThis workflow observes n8n execution errors, normalizes the error payload, and asks Claude to classify the incident. It generates a deduplication hash, searches Jira for a matching issue, then either comments on the existing ticket or creates a new incident. After the Jira action completes, it sends a Slack alert summarizing what happened.\n\n### Setup steps\n\n- Configure the workflow as an n8n error workflow so the Error Trigger receives failed execution data.\n- Add Anthropic credentials and select the intended Claude model for the classification chain.\n- Configure Jira credentials, project/issue settings, and the search query or custom field/label used to match the generated error hash.\n- Configure Slack credentials and choose the destination channel for alerts.\n- Verify that Jira and Slack credentials have permission to search issues, create issues, add comments, and post messages.\n\n### Customization\n\nAdjust the Claude classification prompt, hash-generation logic, Jira project/fields, duplicate search criteria, ticket summary/description templates, and Slack message formatting to match your incident-management conventions.\n\n### Update note (portability fix)\n\nDedup hash generation was moved from `require('crypto')` in a Code node to the native **Crypto** node. This is unrelated to any specific bug report, it's a general portability improvement: self-hosted n8n instances without `NODE_FUNCTION_ALLOW_BUILTIN=crypto` explicitly set would fail on the old approach, and n8n's `@n8n/scan-community-package` verification tool rejects `require('crypto')` outright if this logic is ever repackaged as a community node rather than a workflow template. VERIFY the Crypto node's actual field names (`propertyName`, `encoding`) and hash output format in your own editor before relying on this export, they're inferred from documented UI labels, not confirmed against the raw parameter schema."
      },
      "typeVersion": 1
    },
    {
      "id": "0cd01c07-fd0c-4c01-b3af-baa816a63798",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2480,
        -720
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 336,
        "content": "## Capture and normalize\n\nReceives workflow errors through the error trigger and reshapes the raw error data into a consistent payload for downstream AI analysis."
      },
      "typeVersion": 1
    },
    {
      "id": "fabfacaa-19ff-4928-b13a-a73c63995784",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2016,
        -720
      ],
      "parameters": {
        "color": 7,
        "width": 864,
        "height": 544,
        "content": "## Classify and fingerprint\n\nUses Claude via the Anthropic chat model to classify the normalized error, parses the model output, then hashes a workflowId+category string via the native Crypto node (not a Code node with require('crypto'), for portability) and truncates it for use as a Jira label."
      },
      "typeVersion": 1
    },
    {
      "id": "a2b2ebf3-c177-49a0-ac15-11b2295a8f2d",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1088,
        -720
      ],
      "parameters": {
        "color": 7,
        "width": 640,
        "height": 400,
        "content": "## Check Jira duplicate\n\nSearches Jira for an existing incident matching the generated error hash, prepares duplicate-check routing data, and branches based on whether a ticket already exists."
      },
      "typeVersion": 1
    },
    {
      "id": "2f048774-c919-49c0-8c38-1fae474b8386",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        -832
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 352,
        "content": "## Update existing ticket\n\nHandles the upper duplicate branch by appending a recurrence comment to the existing Jira issue and standardizing the resulting Jira key and action fields."
      },
      "typeVersion": 1
    },
    {
      "id": "c1dc23a5-4be4-4638-89f4-8a1924ab1565",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        -448
      ],
      "parameters": {
        "color": 7,
        "width": 448,
        "height": 368,
        "content": "## Create incident ticket\n\nHandles the lower new-incident branch by creating a Jira ticket and standardizing the new issue key and action fields for later notification."
      },
      "typeVersion": 1
    },
    {
      "id": "55929c5f-30e0-43ba-a31a-9b8e5ccf9120",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        80,
        -720
      ],
      "parameters": {
        "color": 7,
        "width": 688,
        "height": 352,
        "content": "## Merge and alert\n\nConverges the existing-ticket and new-ticket paths, builds a Slack-ready notification message, and sends the final alert to Slack."
      },
      "typeVersion": 1
    },
    {
      "id": "c9debb99-095c-4076-ba1c-85b60031176c",
      "name": "When Error Detected",
      "type": "n8n-nodes-base.errorTrigger",
      "notes": "Global error handler. Assign this workflow as the Error Workflow in Settings \u2192 Error Workflow for every production pipeline. Fires on both execution-level and trigger-level failures.",
      "position": [
        -2432,
        -544
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "beef4254-d444-495e-b4eb-6e3964c24b6f",
      "name": "Normalize Error Data",
      "type": "n8n-nodes-base.code",
      "position": [
        -2208,
        -544
      ],
      "parameters": {
        "jsCode": "const input = $json;\n\nconst workflowId      = input?.workflow?.id ?? 'unknown_workflow_id';\nconst workflowName    = input?.workflow?.name ?? 'Unknown Workflow';\nconst executionId     = input?.execution?.id ?? input?.trigger?.id ?? 'unknown_execution_id';\nconst errorMessage    = input?.execution?.error?.message\n                     ?? input?.trigger?.error?.message\n                     ?? 'No error message captured';\nconst errorStack      = input?.execution?.error?.stack\n                     ?? input?.trigger?.error?.stack\n                     ?? '';\nconst failingNode     = input?.execution?.error?.node?.name\n                     ?? input?.trigger?.error?.node?.name\n                     ?? 'Unknown Node';\nconst failingNodeType = input?.execution?.error?.node?.type\n                     ?? input?.trigger?.error?.node?.type\n                     ?? 'Unknown Type';\nconst errorMode       = input?.execution?.error ? 'execution' : 'trigger';\nconst timestamp       = new Date().toISOString();\n\nconst baseUrl      = $env?.N8N_BASE_URL ?? 'https://your-n8n-instance.com';\nconst executionUrl = `${baseUrl}/workflow/${workflowId}/executions/${executionId}`;\n\nconst stackTruncated = errorStack.length > 4000;\nconst truncatedStack = stackTruncated ? errorStack.substring(0, 4000) : errorStack;\nconst rawPayloadSnippet = JSON.stringify(input).substring(0, 3000);\n\nreturn [{\n  json: {\n    workflowId, workflowName, executionId, errorMessage,\n    errorStack: truncatedStack, stackTruncated,\n    failingNode, failingNodeType, errorMode, timestamp,\n    executionUrl, rawPayloadSnippet\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "67d586b1-d2cf-4787-8c55-a108580a2920",
      "name": "AI Classify Error",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        -1968,
        -544
      ],
      "parameters": {
        "text": "=You are an expert Site Reliability Engineer specializing in n8n workflow automation. Analyze the following workflow execution error and return a structured JSON classification.\n\n## Error Context\n- Workflow: {{ $json.workflowName }} (ID: {{ $json.workflowId }})\n- Execution ID: {{ $json.executionId }}\n- Failing Node: {{ $json.failingNode }} (Type: {{ $json.failingNodeType }})\n- Error Mode: {{ $json.errorMode }}\n- Timestamp: {{ $json.timestamp }}\n\n## Error Message\n```\n{{ $json.errorMessage }}\n```\n\n## Stack Trace{{ $json.stackTruncated ? ' [TRUNCATED AT 4000 chars]' : '' }}\n```\n{{ $json.errorStack }}\n```\n\n## Raw Payload Excerpt\n```json\n{{ $json.rawPayloadSnippet }}\n```\n\n---\n\nReturn ONLY a valid JSON object with this exact schema \u2014 no preamble, no markdown fences:\n{\n  \"category\": \"<one of: RATE_LIMIT | AUTH_FAILURE | SCHEMA_DRIFT | NETWORK_TIMEOUT | CONFIG_ERROR | EMPTY_RESPONSE | PERMISSION_DENIED | UNKNOWN>\",\n  \"severity\": \"<one of: CRITICAL | HIGH | MEDIUM | LOW>\",\n  \"summary\": \"<one concise sentence, max 120 chars, describing the root cause>\",\n  \"root_cause\": \"<2-4 sentences of technical root cause analysis>\",\n  \"recommended_action\": \"<2-3 sentences of concrete remediation steps>\",\n  \"is_transient\": <true if likely self-resolving (rate limits, transient timeouts), false if requires manual fix>\n}\n\nClassification rules:\n- RATE_LIMIT: HTTP 429, quota exceeded, too many requests\n- AUTH_FAILURE: HTTP 401/403, invalid token, expired credentials, API key issues\n- SCHEMA_DRIFT: unexpected field, cannot read property of undefined, JSON parse errors from upstream\n- NETWORK_TIMEOUT: ECONNREFUSED, ETIMEDOUT, ENOTFOUND, socket hang up\n- CONFIG_ERROR: missing required parameter, invalid node configuration, expression evaluation failure\n- EMPTY_RESPONSE: upstream returned 200 but empty body or empty array\n- PERMISSION_DENIED: insufficient scope, ACL rejection, OAuth scope missing\n- UNKNOWN: none of the above\n\nSeverity rules:\n- CRITICAL: data loss risk, payment/compliance workflows, cascading failure\n- HIGH: production pipeline blocked, customer-facing impact\n- MEDIUM: non-critical automation degraded, retryable\n- LOW: dev/test workflows, informational",
        "promptType": "define"
      },
      "typeVersion": 1.4
    },
    {
      "id": "31cdd516-e0c6-472d-88b3-c308ca7c200b",
      "name": "Claude Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
      "notes": "claude-haiku-4-5-20251001: structured extraction from a fixed schema \u2014 Haiku handles it at a fraction of Opus cost and latency. 15s timeout prevents LLM hangs from blocking the pipeline during cascading failures.",
      "position": [
        -1888,
        -320
      ],
      "parameters": {
        "model": "claude-haiku-4-5-20251001",
        "options": {
          "temperature": 0.1
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "848224fe-a7c3-405f-b762-3fc06f8ed3bf",
      "name": "Parse Classification",
      "type": "n8n-nodes-base.code",
      "position": [
        -1632,
        -544
      ],
      "parameters": {
        "jsCode": "const normalized = $('Normalize Error Data').first().json;\nconst claudeResponse = $json?.text ?? $json?.output ?? '';\n\nlet classification;\nlet classificationSource = 'claude';\n\ntry {\n  if (!claudeResponse) throw new Error('Empty response from Claude');\n  const clean = claudeResponse.replace(/```json/gi, '').replace(/```/g, '').trim();\n  const parsed = JSON.parse(clean);\n  if (!parsed.category || !parsed.severity || !parsed.summary) {\n    throw new Error('Missing required fields in Claude response');\n  }\n  classification = parsed;\n} catch (e) {\n  classificationSource = 'fallback';\n  classification = {\n    category: 'UNKNOWN',\n    severity: 'HIGH',\n    summary: `LLM classification unavailable: ${e.message.substring(0, 80)}`,\n    root_cause: `Claude failed to classify this error. Raw response: ${claudeResponse.substring(0, 400)}`,\n    recommended_action: 'Review the raw error in the n8n execution log. LLM classification failed \u2014 check Anthropic API status.',\n    is_transient: false\n  };\n}\n\n// Hashing moved OUT of this node into the native Crypto node downstream\n// ('Hash for Dedup'), instead of require('crypto') here. Reasons: self-hosted\n// instances without NODE_FUNCTION_ALLOW_BUILTIN=crypto set would fail this,\n// and n8n's @n8n/scan-community-package verification tool rejects\n// require('crypto') outright if this logic is ever packaged as a community\n// node rather than a workflow template. This node now only prepares the\n// string to be hashed.\nconst hashInput = `${normalized.workflowId}::${classification.category}`;\n\nconst severityEmoji = { CRITICAL: '\ud83d\udd34', HIGH: '\ud83d\udfe0', MEDIUM: '\ud83d\udfe1', LOW: '\ud83d\udfe2' };\nconst emoji = severityEmoji[classification.severity] ?? '\u26aa';\nconst jiraTitle = `${emoji} [n8n] ${classification.category} \u2014 ${normalized.workflowName}: ${classification.summary}`.substring(0, 255);\n\nreturn [{\n  json: {\n    ...normalized,\n    classification,\n    classificationSource,\n    hashInput,\n    jiraTitle,\n    claudeRaw: claudeResponse\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "ba725d90-b06f-4125-bae4-216bfd57344a",
      "name": "Hash for Dedup",
      "type": "n8n-nodes-base.crypto",
      "notes": "VERIFY before trusting this export: 'propertyName' and 'encoding: HEX' are inferred from n8n's documented UI labels, not confirmed against the raw parameter schema. Run one test execution and confirm the actual output field name and hash format (expect a 64-char hex string) before trusting downstream nodes that read $json.dedupHashFull.",
      "position": [
        -1472,
        -544
      ],
      "parameters": {
        "type": "SHA256",
        "value": "={{ $json.hashInput }}"
      },
      "typeVersion": 1
    },
    {
      "id": "c23d225c-77be-47aa-96fb-f4f93a0296cf",
      "name": "Truncate Hash",
      "type": "n8n-nodes-base.code",
      "position": [
        -1296,
        -544
      ],
      "parameters": {
        "jsCode": "// Original logic used .substring(0, 16) on the hex digest for a\n// shorter Jira label. Preserved here, just moved out of the crypto\n// generation step itself since the native Crypto node doesn't do\n// truncation.\nconst ctx = $input.first().json;\n\nif (!ctx.dedupHashFull) {\n  throw new Error(\n    \"dedupHashFull is missing \u2014 check the 'Hash for Dedup' node's actual \" +\n    \"output property name in your instance, it may differ from what's \" +\n    \"assumed in this export.\"\n  );\n}\n\nreturn [{\n  json: {\n    ...ctx,\n    dedupHash: ctx.dedupHashFull.substring(0, 16)\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "55297765-9e12-43b6-828a-d7be4f7fb385",
      "name": "Search Jira Ticket",
      "type": "n8n-nodes-base.jira",
      "notes": "JQL: finds any open (statusCategory != Done) issue with the dedupHash label. Tickets closed as Won't Fix or Duplicate are statusCategory = Done \u2014 a new error of the same type will create a fresh ticket. This is intentional. Set JIRA_PROJECT_KEY as an n8n env var.",
      "position": [
        -992,
        -544
      ],
      "parameters": {
        "limit": 1,
        "options": {
          "jql": "=project = \"{{ $env.JIRA_PROJECT_KEY }}\" AND labels = \"n8n-dedup-{{ $json.dedupHash }}\" AND statusCategory != Done ORDER BY created DESC",
          "fields": "id,key,status,summary,labels"
        },
        "operation": "getAll"
      },
      "typeVersion": 1
    },
    {
      "id": "ae9a0ce4-4097-4244-b376-2788bd15e498",
      "name": "Check for Duplicate Tickets",
      "type": "n8n-nodes-base.code",
      "position": [
        -816,
        -544
      ],
      "parameters": {
        "jsCode": "// ============================================================\n// Route \u2014 Duplicate Check\n// Single source of truth. Carries full context forward to both\n// IF branches. existingIssueKey and existingIssueId are both\n// stored so the comment node can use whichever the Jira node\n// version requires.\n//\n// NOTE: source node renamed from 'Parse & Hash Classification'\n// to 'Truncate Hash' after splitting hashing into the native\n// Crypto node. Reference updated accordingly.\n// ============================================================\n\nconst allItems = $input.all();\nconst existingIssue = allItems[0]?.json ?? null;\nconst ctx = $('Truncate Hash').first().json;\n\nreturn [{\n  json: {\n    ...ctx,\n    isDuplicate: allItems.length > 0,\n    existingIssueKey: existingIssue?.key ?? null,\n    existingIssueId:  existingIssue?.id  ?? null,\n    existingIssueStatus: existingIssue?.fields?.status?.name ?? null\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "728e504f-d062-4742-9fa7-df0ba9755475",
      "name": "If Ticket Exists",
      "type": "n8n-nodes-base.if",
      "notes": "Output 0 (TRUE) \u2192 open ticket found \u2192 append recurrence comment \u2192 Set Jira Key (comment). Output 1 (FALSE) \u2192 no open ticket \u2192 create new Bug \u2192 Set Jira Key (create). Both paths converge at Merge before Build Slack.",
      "position": [
        -592,
        -544
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-is-duplicate",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.isDuplicate }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "beb73906-a87b-40e3-8e78-4127b4c69816",
      "name": "Append Comment to Jira Ticket",
      "type": "n8n-nodes-base.jira",
      "notes": "operation: 'add' is the correct value per IssueCommentDescription.ts \u2014 'create' does not exist on this resource. Parameter is 'issueKey' and body field is 'comment' (not 'commentBody'). wikiMarkup: true forces API v2 so Atlassian Wiki Markup in the comment body renders correctly on Jira Cloud.",
      "position": [
        -368,
        -656
      ],
      "parameters": {
        "comment": "=\ud83d\udd01 *Recurrence detected* \u2014 {{ $json.timestamp }}\n\n*Execution ID:* {{ $json.executionId }}\n*Failing Node:* {{ $json.failingNode }} ({{ $json.failingNodeType }})\n*Error Mode:* {{ $json.errorMode }}\n*Classification:* {{ $json.classification.category }} / {{ $json.classification.severity }}\n\n*Error Message:*\n{code}{{ $json.errorMessage }}{code}\n\n[View execution log|{{ $json.executionUrl }}]\n\n_Auto-appended by AutomiQ AI Error Observability Workflow_",
        "options": {
          "wikiMarkup": true
        },
        "issueKey": "={{ $json.existingIssueKey }}",
        "resource": "issueComment"
      },
      "typeVersion": 1
    },
    {
      "id": "ca103685-8beb-4ff4-8d4d-d2a9d863091a",
      "name": "Create Jira Incident Ticket",
      "type": "n8n-nodes-base.jira",
      "notes": "Creates a new Jira Bug. Labels: n8n-error, n8n-dedup-{hash} (JQL anchor), error category, transient flag, and llm-fallback when Claude was unavailable (JQL: project = X AND labels = 'llm-fallback' to audit LLM availability).",
      "position": [
        -368,
        -272
      ],
      "parameters": {
        "project": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.JIRA_PROJECT_KEY }}"
        },
        "summary": "={{ $json.jiraTitle }}",
        "issueType": {
          "__rl": true,
          "mode": "name",
          "value": "Bug"
        },
        "additionalFields": {
          "labels": "=n8n-error,n8n-dedup-{{ $json.dedupHash }},{{ $json.classification.category }},{{ $json.classification.is_transient ? 'transient' : 'requires-manual-fix' }}{{ $json.classificationSource === 'fallback' ? ',llm-fallback' : '' }}",
          "priority": "={{ $json.classification.severity === 'CRITICAL' ? 'Highest' : $json.classification.severity === 'HIGH' ? 'High' : $json.classification.severity === 'MEDIUM' ? 'Medium' : 'Low' }}",
          "description": "=*Workflow:* [{{ $json.workflowName }}|{{ $json.executionUrl }}]\n*Workflow ID:* {{ $json.workflowId }}\n*Execution ID:* {{ $json.executionId }}\n*Failing Node:* {{ $json.failingNode }} ({{ $json.failingNodeType }})\n*Error Mode:* {{ $json.errorMode }}\n*Timestamp:* {{ $json.timestamp }}\n*Dedup Hash:* {{ $json.dedupHash }}\n*Classification Source:* {{ $json.classificationSource }}\n*Transient:* {{ $json.classification.is_transient ? 'Yes \u2014 likely self-resolving' : 'No \u2014 manual intervention required' }}\n\nh2. Root Cause Analysis\n{{ $json.classification.root_cause }}\n\nh2. Recommended Action\n{{ $json.classification.recommended_action }}\n\nh2. Raw Error Message\n{code}{{ $json.errorMessage }}{code}\n\nh2. Stack Trace\n{code:java}{{ $json.errorStack }}{code}\n\n----\n_Auto-generated by AutomiQ AI Error Observability Workflow_"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "5e99b93a-b00c-4f59-a37e-b496a9ed60ea",
      "name": "Prepare Comment Branch Data",
      "type": "n8n-nodes-base.set",
      "notes": "Stamps jiraKey and action onto the item from the comment branch. Merge node downstream reads $json.jiraKey regardless of which branch ran.",
      "position": [
        -144,
        -656
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "assign-jira-key-comment",
              "name": "jiraKey",
              "type": "string",
              "value": "={{ $json.existingIssueKey ?? 'UNKNOWN' }}"
            },
            {
              "id": "assign-action-comment",
              "name": "action",
              "type": "string",
              "value": "Recurrence appended to existing ticket"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.3
    },
    {
      "id": "162179e5-0c8c-4ee2-831e-110504daf466",
      "name": "Prepare Create Branch Data",
      "type": "n8n-nodes-base.set",
      "notes": "Stamps jiraKey ($json.key from Jira create response) and action onto the item from the create branch. Merge node downstream reads $json.jiraKey regardless of which branch ran.",
      "position": [
        -144,
        -272
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "assign-jira-key-create",
              "name": "jiraKey",
              "type": "string",
              "value": "={{ $json.key ?? 'UNKNOWN' }}"
            },
            {
              "id": "assign-action-create",
              "name": "action",
              "type": "string",
              "value": "New ticket created"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.3
    },
    {
      "id": "072ce769-de69-484e-9b06-f6b090b8b9dd",
      "name": "Merge Data from Branches",
      "type": "n8n-nodes-base.merge",
      "notes": "chooseBranch mode: forwards whichever input arrives without waiting for the other. Since only one branch (comment or create) executes per run, this is the correct mode \u2014 append and combine both wait for all inputs. Build Slack Notification reads $json.jiraKey which is always stamped by the Set nodes upstream.",
      "position": [
        128,
        -544
      ],
      "parameters": {
        "mode": "chooseBranch"
      },
      "typeVersion": 3
    },
    {
      "id": "e8e3aae0-7a07-41d9-95b6-3583380a9281",
      "name": "Build Slack Message",
      "type": "n8n-nodes-base.code",
      "position": [
        352,
        -544
      ],
      "parameters": {
        "jsCode": "const item = $json;\n\n// jiraKey and action are always present \u2014 stamped by Set nodes upstream\nconst jiraKey = item.jiraKey ?? 'UNKNOWN';\nconst action  = item.action  ?? 'Unknown action';\n\nconst jiraBase = $env?.JIRA_BASE_URL ?? 'https://your-domain.atlassian.net/browse';\nconst jiraUrl  = `${jiraBase}/${jiraKey}`;\n\nconst severityColor = { CRITICAL: '#FF0000', HIGH: '#FF6600', MEDIUM: '#FFAA00', LOW: '#36A64F' };\nconst severityEmoji = { CRITICAL: ':red_circle:', HIGH: ':large_orange_circle:', MEDIUM: ':large_yellow_circle:', LOW: ':large_green_circle:' };\nconst color = severityColor[item.classification.severity] ?? '#808080';\nconst emoji = severityEmoji[item.classification.severity] ?? ':white_circle:';\n\nconst fallbackTag = item.classificationSource === 'fallback' ? ' [FALLBACK]' : '';\nconst fallbackNote = item.classificationSource === 'fallback' ? ' _(fallback \u2014 LLM unavailable)_' : '';\n\nconst slackPayload = {\n  text: `${emoji} *n8n Error:* ${item.workflowName} \u2014 ${item.classification.category}`,\n  attachments: [\n    {\n      color,\n      blocks: [\n        {\n          type: 'header',\n          text: { type: 'plain_text', text: `${emoji} ${item.classification.severity} | ${item.classification.category}${fallbackTag}`, emoji: true }\n        },\n        {\n          type: 'section',\n          text: { type: 'mrkdwn', text: `*${item.classification.summary}*${fallbackNote}` }\n        },\n        {\n          type: 'section',\n          fields: [\n            { type: 'mrkdwn', text: `*Workflow:*\\n<${item.executionUrl}|${item.workflowName}>` },\n            { type: 'mrkdwn', text: `*Failing Node:*\\n${item.failingNode}` },\n            { type: 'mrkdwn', text: `*Error Mode:*\\n${item.errorMode}` },\n            { type: 'mrkdwn', text: `*Transient:*\\n${item.classification.is_transient ? 'Yes' : 'No \u2014 manual fix required'}` }\n          ]\n        },\n        {\n          type: 'section',\n          text: { type: 'mrkdwn', text: `*Root Cause:*\\n${item.classification.root_cause}` }\n        },\n        {\n          type: 'section',\n          text: { type: 'mrkdwn', text: `*Recommended Action:*\\n${item.classification.recommended_action}` }\n        },\n        {\n          type: 'context',\n          elements: [\n            { type: 'mrkdwn', text: `Dedup Hash: \\`${item.dedupHash}\\` | ${action}: <${jiraUrl}|${jiraKey}> | ${item.timestamp}` }\n          ]\n        },\n        {\n          type: 'actions',\n          elements: [\n            { type: 'button', text: { type: 'plain_text', text: 'View Execution' }, url: item.executionUrl, style: 'primary' },\n            { type: 'button', text: { type: 'plain_text', text: `Jira ${jiraKey}` }, url: jiraUrl }\n          ]\n        }\n      ]\n    }\n  ]\n};\n\nreturn [{ json: { slackPayload, jiraKey, jiraUrl } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "b2f01b19-ebe4-4664-866b-ffcf4a8fc35e",
      "name": "Send Alert to Slack",
      "type": "n8n-nodes-base.slack",
      "notes": "Slack Bot Token credential with chat:write scope. Set SLACK_CHANNEL_ID as an n8n env var.",
      "position": [
        576,
        -544
      ],
      "parameters": {
        "select": "channel",
        "blocksUi": "={{ JSON.stringify($json.slackPayload.attachments[0].blocks) }}",
        "channelId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $env.SLACK_CHANNEL_ID }}"
        },
        "messageType": "block",
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "d3d539d2-fe83-41b7-8ad5-26cd180752ed",
  "nodeGroups": [],
  "connections": {
    "Claude Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Classify Error",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Truncate Hash": {
      "main": [
        [
          {
            "node": "Search Jira Ticket",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hash for Dedup": {
      "main": [
        [
          {
            "node": "Truncate Hash",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Ticket Exists": {
      "main": [
        [
          {
            "node": "Append Comment to Jira Ticket",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create Jira Incident Ticket",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Classify Error": {
      "main": [
        [
          {
            "node": "Parse Classification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Jira Ticket": {
      "main": [
        [
          {
            "node": "Check for Duplicate Tickets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Slack Message": {
      "main": [
        [
          {
            "node": "Send Alert to Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When Error Detected": {
      "main": [
        [
          {
            "node": "Normalize Error Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Error Data": {
      "main": [
        [
          {
            "node": "AI Classify Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Classification": {
      "main": [
        [
          {
            "node": "Hash for Dedup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Data from Branches": {
      "main": [
        [
          {
            "node": "Build Slack Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Create Branch Data": {
      "main": [
        [
          {
            "node": "Merge Data from Branches",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Check for Duplicate Tickets": {
      "main": [
        [
          {
            "node": "If Ticket Exists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Jira Incident Ticket": {
      "main": [
        [
          {
            "node": "Prepare Create Branch Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Comment Branch Data": {
      "main": [
        [
          {
            "node": "Merge Data from Branches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Comment to Jira Ticket": {
      "main": [
        [
          {
            "node": "Prepare Comment Branch Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

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

This global error-handling workflow classifies n8n execution failures with Anthropic Claude, deduplicates incidents in Jira using a hash label, and posts a detailed alert to Slack with links to the affected execution and Jira issue. Triggers whenever any n8n workflow execution…

Source: https://n8n.io/workflows/16646/ — 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 automates Invoice & Payment Tracking (with Approvals) across Notion and Slack. Ingest — You drop invoices/receipts (PDF/IMG/JSON) into the flow. Extract — OCR + parsing pulls out key fie

HTTP Request, Chain Llm, Anthropic Chat +5
AI & RAG

Content - Newsletter Agent. Uses formTrigger, chainLlm, outputParserStructured, httpRequest. Event-driven trigger; 91 nodes.

Form Trigger, Chain Llm, Output Parser Structured +8
AI & RAG

Content - Newsletter Agent. Uses formTrigger, chainLlm, outputParserStructured, httpRequest. Event-driven trigger; 87 nodes.

Form Trigger, Chain Llm, Output Parser Structured +7
AI & RAG

Agent Nodes. Uses lmChatOpenAi, slack, stopAndError, errorTrigger. Event-driven trigger; 72 nodes.

OpenAI Chat, Slack, Stop And Error +12
AI & RAG

Content - Write Best Tools In Category Article. Uses formTrigger, httpRequest, slack, chainLlm. Event-driven trigger; 41 nodes.

Form Trigger, HTTP Request, Slack +6