mcpField
N
n8n-error-handling

n8n-error-handling

github.com/czlonkowski开发23.2k44 次安装3 天前更新
代码仓库

关于 n8n-error-handling

面向构建无人值守及 API 工作流的 n8n 用户,介绍如何让失败可见、可恢复并在适当情况下自动重试。重点涵盖节点错误输出与工作流级错误工作流。

主要功能

  1. 01配置节点级错误输出
  2. 02设置网络节点重试
  3. 03构建工作流级错误捕获
  4. 04按原因返回 HTTP 状态码
  5. 05校验输入并生成结构化错误

适用场景

  1. 01为 Webhook API 返回恰当错误
  2. 02在定时任务失败时发送告警
  3. 03从暂时性网络故障中恢复

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/czlonkowski/n8n-mcp --skill n8n-error-handling

安装后在 Claude Code 中输入/n8n-error-handling确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

API_WORKFLOWS.md
ERROR_WORKFLOWS.md
NODE_ERROR_OUTPUTS.md
README.md
RESPONSE_SHAPES.md

详细介绍

n8n 错误处理

默认情况下,n8n 节点抛出错误时,整个工作流都会停止。对于你正在查看的交互式运行,这没问题——你会看到红色节点并修复它。但对于任何无人值守的场景(Webhook API、Cron 作业、队列工作器、Agent 工具),这就不是合适的默认行为:调用方会遇到超时或收到空的 500 响应,运维人员不会收到告警,而症状只是“集成突然停止工作”,既没有日志,也没有线索。

这项技能旨在让故障清晰可见、结构化且可恢复,而最理想的情况是还能自动修复,让短暂故障根本无需人工介入。

以下两个做法可以防止大多数静默故障:

  • 按节点配置错误输出——节点失败时,会沿着你控制的第二个输出路由,而不是终止本次运行。
  • 工作流级错误工作流——用于捕获所有未被按节点处理的异常(超时、节点之间崩溃、未连接的故障)。

何时确实需要这样做

工作流类型错误处理要求
Webhook / API(任何包含 Respond to Webhook 的工作流)必需。 每个可能失败的节点都要连接错误输出;状态码要与故障原因相符。
定时 / Cron / 队列工作器 / Agent 工具(无人值守)必需。 配置工作流级错误工作流,并为网络节点设置 retryOnFail。
你自己运行并监控的一次性内部工作流可选。 默认的 onError: "stopWorkflow" 即可——你会看到红色节点,然后重新运行。

界限在于:如果除了你之外还有其他人会看到输出——下游系统、终端用户或值班工程师——就必须处理故障,而不能将其忽略。如果只有你在监控,并且故障的代价只是“我注意到后重新运行”,那么宽松一些也没关系。


第一大静默陷阱:按节点配置错误输出需要两个步骤

这是 n8n 工作流“处理”错误但实际上将其吞掉的最常见原因。要将节点故障路由到处理器,需要进行两项更改;只做其中一项看似配置完整,实际却会出问题:

  1. 在节点上设置 onError: "continueErrorOutput"。这一步会创建第二个输出。没有它,无论你如何连线,main[1] 都不存在。
  2. 将错误输出连到真实的处理器(connections.<node>.main[1],即 sourceIndex: 1)。如果没有目标,错误数据就会被发送到虚空中。

只做其中一项,就会遇到以下故障模式:

你做了什么运行时会发生什么
设置了 onError,但未连接错误输出错误数据会被静默丢弃。下游不会触发。仪表板会将本次运行显示为成功。这是最糟糕的情况——错误不会记录在任何地方。
连接了错误输出,但未设置 onError该输出口永远不会触发,处理器也无法访问。发生故障时,工作流只会停止(默认的 stopWorkflow)。
两项都完成故障会沿着 main[1] 路由到你的处理器。✅
使用 n8n_update_partial_workflow 同时完成两项设置
// 1) Turn on the error output (creates main[1])
{ type: "updateNode", nodeName: "HTTP Request",
  changes: { onError: "continueErrorOutput" } }

// 2) Wire the error output to a handler. sourceIndex: 1 = the error output.
{ type: "addConnection",
  source: "HTTP Request",
  target: "Handle Error",
  sourceIndex: 1 }

sourceIndex: 0 是成功路径,sourceIndex: 1 是错误路径。(对于 IF 节点,别名 branch: "true"/"false" 对应索引 0/1;对于通用的易故障节点,请显式使用 sourceIndex: 1。)

然后进行验证。 这个陷阱不会被 validate_workflow 检出——错误输出只连接了一半,验证结果仍会显示正常。使用 n8n_get_workflow 拉取工作流,并确认以下两项都已配置:

  • 节点的 onError 为 "continueErrorOutput"。
  • connections["HTTP Request"].main[1] 包含你的处理器。

有效的 onError 值:

值效果
"stopWorkflow"(默认)错误会终止整个工作流。
"continueRegularOutput"错误项会流入正常输出。少见且通常不合适——下游会收到错误格式的数据并继续运行。
"continueErrorOutput"错误项会流入单独的错误输出(main[1])。你需要连接的就是这个输出。

完整的故障模式目录、汇聚 / 扇出结构及验证方法:NODE_ERROR_OUTPUTS.md。


先实现自我修复:配置 retryOnFail,再连接错误路径

构建错误分支之前,先吸收瞬时故障,避免它们触发这些分支。对于任何调用网络服务的节点——HTTP Request、通信服务(Gmail/Slack/Discord)、数据库、AI 节点、第三方集成——都要设置节点级重试:

{ type: "updateNode", nodeName: "HTTP Request",
  changes: {
    retryOnFail: true,
    maxTries: 3,
    waitBetweenTries: 5000   // ms
  } }

为什么要先做这件事:遇到 429 或上游短暂故障时,节点会重试,通常可以自行成功。这样,错误输出只会在真实且持续的故障时触发,让 5xx 响应和待命告警反映实际问题,而不是噪声。

需要了解的引擎限制:重试会在任何错误时触发(不支持按状态码筛选),maxTries 上限为 5,waitBetweenTries 上限为 5000ms——因此 5000 既是最大值,也是合理的默认值。节点专属说明请参阅 n8n-node-configuration(NODE_FAMILY_GOTCHAS.md)。


API 工作流:标准结构

由 webhook 触发并向调用方返回响应的工作流,必须遵循一条优先级最高的规则:不能有悬空分支。每条路径——成功路径和所有错误路径——都必须以 Respond to Webhook 结束,否则调用方会一直等待,直到请求超时。

Webhook (responseMode: "responseNode")
  ├── validate input → process → Respond (200, body)
  └── (any fallible node's error output → sourceIndex 1)
            → Respond (4xx/5xx, structured error body)
            → optional: log full error privately / notify

要实现这一点,需做好三件事:

  1. 将多条路径汇入一个错误响应节点。 多个可能失败的节点可以将其 main[1] 路由到同一个 Respond 节点,让图表保持清晰易读。
  2. 在上游检查验证失败(4xx),而不是通过错误输出处理。 缺少字段不代表节点崩溃——这是预期结果,且有明确的响应。使用 IF/Switch(或下文的 schema 验证器)进行分支,并直接返回 400/401/403/404。错误输出用于处理意外故障(5xx)。
  3. responseCode 默认值为 200——即使在错误分支上也是如此。 这是一个容易忽视的陷阱(参阅 RESPONSE_SHAPES.md 和 n8n-node-configuration NODE_FAMILY_GOTCHAS.md):错误分支返回 200 和错误正文时,调用方的 HTTP 客户端会将其视为成功,因此不会触发错误处理。请在每个 Respond 节点上显式设置 responseCode。
输入验证:Set 节点 schema 验证器

对于任何需要验证结构化输入的端点,都应在单个 Set 节点中通过 IIFE 执行检查,而不是针对每个字段串联 IF/Switch 节点。一个节点即可验证整个载荷,并返回 { valid, validationError, details, requiredSchema };再通过 IF 根据 valid 进行分支:有效时进入你的逻辑(200),无效时进入 400 Respond,并回显 schema,让调用方能够自行修正。这种方式也比在 Code 节点中使用递归验证器并搭配子工作流快得多。完整模式、约束说明和表达式转义注意事项请参阅 API_WORKFLOWS.md。


响应结构:将原因映射到状态码

返回 text/plain "Internal Server Error" 的 5xx 在技术上是错误响应,实际上毫无用处。而且并非每种失败都是 5xx。根据请求失败的原因匹配状态码,因为调用方会据此分支处理:他们的监控会针对 5xx(你的问题)触发告警,但不会针对 4xx(他们的问题);5xx 暗示“重试”,而 4xx 暗示“不要重试”。

**常见错误:**把所有情况(包括无效输入)都接到同一个 Respond 上,让它返回 500 internal_error。这样调用方就无法区分是他们的错误还是你的服务中断,你的错误率也无法将真实事故与客户端噪声区分开来。

原因状态码error 代码处理位置
缺少必填字段/类型错误400validation_error上游检查(架构验证器/IF),而不是错误输出
缺少身份验证信息或信息无效401unauthorized上游检查
已通过身份验证,但没有权限403forbidden上游检查
请求中的资源 ID 有效,但你的数据中不存在404not_found根据查找的结果分支,而不是根据其错误分支
与当前状态冲突(重复、竞态)409conflict通过逻辑检测
调用方超出速率限制429rate_limit_exceeded设置 Retry-After 标头
节点抛出错误,原因未知500internal_error错误输出路径
第三方 API 返回错误502upstream_errorHTTP 节点的错误输出
当前无法处理(下游不可用)503service_unavailable检测具体错误,提示重试
第三方 API 超时504upstream_timeout根据消息筛选错误输出

因此,这里有两种不同的流程:4xx 在工作开始前决定(IF/Switch + 专用 Respond),5xx 来自错误输出(“我们尝试了,但失败了”)。

**使用一个 Respond,通过表达式设置代码。**如果错误路径的差异仅在于数字和消息(正文结构和标头相同),就不要通过 Switch 分流到 N 个 Respond 节点。Respond 节点的 Response Code 和正文都支持表达式——直接内联计算代码:

// Response Code field on a single Respond to Webhook:
{{ (() => {
    const msg = $json.error?.message || $json.message || '';
    if (msg.includes('INVALID_ID')) return 400;
    if (/429|too many/i.test(msg)) return 429;
    if (/timeout/i.test(msg))      return 504;
    if (/upstream|llm|api/i.test(msg)) return 502;
    return 500;
})() }}

只有在路径的结构不同时(标头、正文结构不同,或需要重定向),才使用 Switch + 多个 Respond。同一结构只更改数字时,使用一个由表达式驱动的 Respond 即可。

默认响应格式为 { "error": "<code>", "message": "<human text>" }——HTTP 状态码已经表明请求成功还是失败,因此不需要 ok: false 标志。绝不要泄露内部信息(堆栈跟踪、SQL、上游响应正文、令牌)到响应中——将这些信息私下记录,并返回经过清理的消息。关联 ID、retry_after、验证 details 以及完整的禁止泄露清单,请参阅 RESPONSE_SHAPES.md。


工作流级错误工作流(兜底处理)

节点输出负责处理你预料到、且记得接线的节点上发生的失败。错误工作流则会捕获其他所有情况:忘记接线的节点、节点之间发生的崩溃、整个工作流超时、触发器失败。对于无人值守的工作流,它就像一道安全网,能把“它悄无声息地停止了”变成“收到了一条警报”。

将它构建为一个独立的工作流,并以 Error Trigger 节点作为起点。n8n 会在失败时将上下文传给它:

{
  "execution": { "id": "...", "url": "...", "lastNodeExecuted": "Fetch order",
    "error": { "name": "NodeApiError", "message": "...", "timestamp": 1715000000000 } },
  "workflow": { "id": "...", "name": "Sync Stripe customers" }
}

最简版本——捕获 → 通知:

Error Trigger → Set (build alert from execution + error) → Slack/email (post to #incidents)

一条好的警报应包含工作流名称、指向编辑器和失败执行记录的链接、失败节点名称,以及真实的错误消息(而不是“Workflow failed”)。字段表达式,以及“通过 n8n 节点获取失败输入”的可选升级方案,见 ERROR_WORKFLOWS.md。

请特别留意以下两个陷阱:

  • 递归陷阱。 如果错误工作流通过 Slack 发送通知,而 Slack 恰好不可用,错误工作流也会失败——原始错误也会随之消失。请使用与你监控的工作流不同的渠道发送通知(大多数工作流通过 Slack 告警 → 错误工作流使用电子邮件),并添加备用方案(写入 Data Table),这样即使通知失败,也会留下记录。
  • 已“处理”的错误不会继续向上冒泡。 如果某个节点的错误输出连接到了一个会丢弃数据的空操作节点,n8n 就会将该错误视为已处理,错误工作流也不会触发。只有在确实要对错误进行处理时,才按节点捕获错误。

**社区 MCP 无法执行的操作:**设置错误工作流(实例默认设置或按工作流覆盖)属于 n8n UI 设置——Workflow Settings → Error Workflow。没有可用于此操作的 MCP 工具。使用 MCP 构建错误工作流后,告知用户要在哪个 UI 位置进行设置,并提醒他们为每个无人值守的工作流重复此操作(或设置实例默认值)。


社区 MCP 不提供的功能

想要执行的操作实际情况
设置工作流的 Error Workflow 选项仅限 UI(Workflow Settings → Error Workflow)。没有 MCP 工具。构建工作流后,告知用户相应的 UI 操作步骤。
切换其他工作流设置(Save Execution Data、时区、超时、调用者策略)仅限 UI。n8n_update_partial_workflow 有 updateSettings,但错误工作流分配并不一定能通过它可靠地设置——请在 UI 中确认。
启用实例级错误日志记录(Sentry、服务器日志)属于实例配置,完全在 n8n 工作流之外。

MCP 可以执行的操作:构建错误工作流、为节点设置 onError/retryOnFail(updateNode/patchNodeField)、连接错误输出(addConnection,并设置 sourceIndex: 1)、验证(validate_workflow、n8n_validate_workflow)、自动修复常见问题(n8n_autofix_workflow)、测试(n8n_test_workflow),以及检查失败(n8n_executions)。


反模式

反模式问题修复方法
设置了 onError,但错误输出未接线错误被静默丢弃;运行显示为成功将 sourceIndex: 1 接到实际处理程序,或将 onError 恢复为 stopWorkflow,以便明确暴露错误
错误输出已接线,但未设置 onError插槽永远不会触发;处理程序无法访问;工作流遇到失败时会停止设置 onError: "continueErrorOutput"
Webhook → 处理 → 响应,没有错误分支调用方收到超时或 n8n 的通用 500 错误将每个可能失败节点的错误输出接到一个 Respond
错误分支以 {error} 正文返回 200调用方客户端会认为操作成功;其错误处理逻辑永远不会触发在错误 Respond 中将 responseCode 显式设为 4xx/5xx
所有情况都返回同一个 500 internal_error调用方无法区分输入错误和服务中断将原因映射到状态码(调用方问题用 4xx,你的问题用 5xx)
在 Code 节点中捕获错误并将其作为数据返回下游会处理形似错误的数据并继续执行让错误抛出;使用 onError: "continueErrorOutput" 并接好对应路径
网络节点未设置 retryOnFail每次临时 429/网络抖动都会表现为 5xx;告警会因噪声触发retryOnFail: true, maxTries: 3, waitBetweenTries: 5000
Switch → 多个仅状态码不同的 Respond本来一个 Respond 就够,却用了 5 个节点在单个由表达式驱动的 Respond 中内联计算状态码
无人值守工作流没有错误工作流真正发生故障时不会有任何处理创建 Error Trigger 工作流,并在 UI 中为其指定错误工作流
错误工作流通知工作流监控使用的同一个频道频道宕机 → 错误工作流也失败 → 错误消失使用不同的频道,并添加 Data Table 备用方案
将 $json.error(堆栈/SQL/令牌)泄露到响应中向调用方/攻击者暴露内部信息私下记录日志,返回经过净化的消息

参考文件

文件何时阅读
NODE_ERROR_OUTPUTS.md为单个可能失败的节点接线其专属错误输出时
API_WORKFLOWS.md构建/审查 webhook → Respond 工作流时,包括架构验证器
RESPONSE_SHAPES.md定义响应正文约定、状态码以及不能泄露的内容时
ERROR_WORKFLOWS.md为无人值守工作流设置工作流级兜底捕获时

与其他技能的集成

  • n8n-workflow-patterns — webhook/API 和定时模式是错误处理的主要所在。整体结构使用它;加固则使用本技能。
  • n8n-node-configuration — onError/retryOnFail 属于节点配置;NODE_FAMILY_GOTCHAS.md 深入介绍了 Webhook/Respond 响应码陷阱。
  • n8n-validation-expert — 错误输出只接好一半(两个步骤缺了一个)属于连接/配置审计项,而不是验证错误。本技能提供修复方法。
  • n8n-expression-syntax — 由表达式驱动的 Response Code 和告警消息表达式依赖正确的 {{ }} 语法以及对 $json.error 的访问。
  • n8n-code-javascript / n8n-code-python — 如果你在 Code 节点内部捕获错误,请有意决定:重新抛出以使用错误输出,还是处理后继续。不要返回形似错误的数据,却假装执行成功。
  • n8n-code-tool — Agent 的 Code Tool 会将抛出的错误传回大模型,由大模型重试;这与工作流节点使用的是不同的错误契约。
  • n8n-binary-and-data — 文件/二进制操作同样可能失败;像网络节点一样接好它们的错误输出。

快速参考清单

对于API / webhook工作流:

  • Webhook 触发器使用 responseMode: "responseNode"
  • 在上游验证输入 → 由 Respond 返回 4xx(使用 schema validator 或 IF)
  • 每个可能失败的节点都设置 onError: "continueErrorOutput",并且连接了 main[1]
  • 网络节点设置 retryOnFail: true, maxTries: 3, waitBetweenTries: 5000
  • 错误路径以一个设置了明确 4xx/5xx responseCode 的 Respond 结束
  • 状态码与原因匹配(调用方导致的错误用 4xx,你方导致的错误用 5xx)
  • 错误正文为 { error, message }——不包含堆栈跟踪、SQL 或令牌
  • 使用 n8n_get_workflow 验证:每个可能失败的节点都设置了 onError,并连接了 main[1]

对于无人值守(定时 / cron / 队列)工作流:

  • 网络节点配置了 retryOnFail
  • 已创建 Error Trigger 工作流(捕获 → 通知,可选重试)
  • 错误工作流通过不同的渠道发送通知,并设置了备用方案(避免递归陷阱)
  • 已在 n8n UI 中分配错误工作流设置(MCP 无法完成此操作——提醒用户)

请记住:默认情况是静默。错误处理分两步——让失败能够流转(为每个节点设置 onError 并连接输出,或使用全捕获错误工作流),再让它发声(用状态码和正文如实说明情况)。只做一步比不做更糟,因为看起来像是已经处理好了。

评论

支持 Markdown