{ "kind": "open-flow-workflow", "version": 1, "name": "n8n Execution Diagnostics", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_153b53123b3c550d": { "name": "Execution details", "kind": "start", "inputDefs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "workflow" }, { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "execution" } ] }, "n_32cbe9a8edc7aeb2": { "inputs": { "workflow": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_153b53123b3c550d", "output": "workflow" } ] }, "execution": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_153b53123b3c550d", "output": "execution" } ] } }, "name": "Classify execution errors", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "workflow" }, { "jsonSchema": { "type": "object" }, "nullable": false, "handle": "execution" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "workflowName" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "lastNodeExecuted" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "failureCount" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "primaryCategory" }, { "jsonSchema": { "type": "array", "items": { "type": "object" } }, "nullable": false, "handle": "failures" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "diagnosticStatus" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "privacyNote" } ], "name": "Classify execution errors", "moduleId": "n_819134d40004d26e", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_2e0f09e4aab6e02a": { "name": "Execution diagnosis", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "workflowName" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "lastNodeExecuted" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "failureCount" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "primaryCategory" }, { "jsonSchema": { "type": "array", "items": { "type": "object" } }, "nullable": false, "handle": "failures" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "diagnosticStatus" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "privacyNote" } ], "outputMappings": { "workflowName": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_32cbe9a8edc7aeb2", "output": "workflowName" } ] }, "lastNodeExecuted": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_32cbe9a8edc7aeb2", "output": "lastNodeExecuted" } ] }, "failureCount": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_32cbe9a8edc7aeb2", "output": "failureCount" } ] }, "primaryCategory": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_32cbe9a8edc7aeb2", "output": "primaryCategory" } ] }, "failures": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_32cbe9a8edc7aeb2", "output": "failures" } ] }, "diagnosticStatus": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_32cbe9a8edc7aeb2", "output": "diagnosticStatus" } ] }, "privacyNote": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_32cbe9a8edc7aeb2", "output": "privacyNote" } ] } } } }, "edges": [ { "source": "n_153b53123b3c550d", "target": "n_32cbe9a8edc7aeb2" }, { "source": "n_32cbe9a8edc7aeb2", "target": "n_2e0f09e4aab6e02a" } ] } }, "modules": { "n_819134d40004d26e": { "name": "Classify execution errors", "imports": [], "source": "export default function diagnose({workflow, execution}) {\n\nif (!workflow || !Array.isArray(workflow.nodes)) {\n throw new Error('workflow.nodes must be an array');\n}\nif (!execution || typeof execution !== 'object' || Array.isArray(execution)) {\n throw new Error('execution must be an object');\n}\n\nconst nodeTypes = new Map(\n workflow.nodes.map((node) => [String(node.name || ''), String(node.type || '')]),\n);\nconst data = execution.data && typeof execution.data === 'object' ? execution.data : {};\nconst result = data.resultData && typeof data.resultData === 'object'\n ? data.resultData\n : (execution.resultData && typeof execution.resultData === 'object' ? execution.resultData : {});\nconst lastNode = result.lastNodeExecuted ? String(result.lastNodeExecuted) : null;\nconst runData = result.runData && typeof result.runData === 'object' ? result.runData : {};\nconst failures = [];\nfunction safeMessage(value) {\n let text = String(value ?? '').replace(/\\s+/g, ' ').trim();\n text = text.replace(/bearer\\s+[A-Za-z0-9._~+/-]{8,}/gi, 'Bearer [REDACTED]');\n text = text.replace(/(api[_-]?key|token|secret|password|authorization)\\s*[:=]\\s*(?:bearer\\s+)?[^\\s,;}]+/gi, '$1=[REDACTED]');\n return text.slice(0, 300);\n}\n\nfunction classify(message, status) {\n const text = `${String(status ?? '')} ${message}`.toLowerCase();\n if (['401', '403', 'unauthorized', 'forbidden', 'credential', 'authentication'].some((x) => text.includes(x))) {\n return ['authentication', 'Verify credential reference, auth scheme, token scope and expiry; then retest only the failing node.'];\n }\n if (['429', 'rate limit', 'too many requests'].some((x) => text.includes(x))) {\n return ['rate_limit', 'Add bounded retry with exponential backoff and respect Retry-After or the upstream rate-limit policy.'];\n }\n if (['timeout', 'timed out', 'etimedout', 'econnaborted'].some((x) => text.includes(x))) {\n return ['timeout', 'Set an explicit timeout, retry only idempotent work, and isolate the slow external call from the main path.'];\n }\n if (['econn', 'dns', 'network', 'connection refused', 'socket hang up'].some((x) => text.includes(x))) {\n return ['network', 'Check endpoint reachability, DNS and TLS first; then add bounded retries and an explicit failure path.'];\n }\n if (['400', '422', 'invalid', 'validation', 'bad request'].some((x) => text.includes(x))) {\n return ['invalid_input', 'Inspect the outbound payload/schema and validate required fields before the failing node.'];\n }\n if (['expression', 'undefined', 'cannot read', 'not defined', 'item linking'].some((x) => text.includes(x))) {\n return ['expression', 'Validate referenced fields, item linking and optional values before evaluating the expression.'];\n }\n return ['execution_error', 'Reproduce the failing node with the same sanitized input, then add an explicit error path before re-enabling automation.'];\n}\n\nfunction addFailure(node, rawError) {\n const error = rawError && typeof rawError === 'object' ? rawError : {};\n const rawMessage = error.message || error.description || error.name || rawError || 'Execution failed';\n const statusCode = error.httpCode ?? error.statusCode ?? error.status ?? null;\n const message = safeMessage(rawMessage);\n const [category, recommendedNextStep] = classify(message, statusCode);\n failures.push({\n node,\n nodeType: node ? (nodeTypes.get(String(node)) || null) : null,\n category,\n statusCode,\n message,\n recommendedNextStep,\n });\n}\nfor (const [nodeName, runs] of Object.entries(runData)) {\n if (!Array.isArray(runs)) continue;\n for (const run of runs) {\n if (!run || typeof run !== 'object' || !run.error) continue;\n addFailure(nodeName, run.error);\n }\n}\nif (result.error && failures.length === 0) {\n addFailure(lastNode, result.error);\n}\n\nconst counts = {};\nfor (const failure of failures) {\n counts[failure.category] = (counts[failure.category] || 0) + 1;\n}\nconst primaryCategory = Object.entries(counts)\n .sort((a, b) => b[1] - a[1])[0]?.[0] || 'unknown';\n\nreturn {\n workflowName: String(workflow.name || ''),\n lastNodeExecuted: lastNode,\n failureCount: failures.length,\n primaryCategory,\n failures,\n diagnosticStatus: failures.length ? 'FAILED_EXECUTION_ANALYZED' : 'NO_STRUCTURED_ERROR_FOUND',\n privacyNote: 'Common secret-like strings are redacted and error messages are truncated.',\n};\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }