mcpField
N
n8n-error-handling

n8n-error-handling

github.com/czlonkowski开发6.5k2.1k 次安装4 天前更新
代码仓库

关于 n8n-error-handling

这份参考技能介绍如何为 n8n 工作流设计错误处理,尤其适用于无人值守的 Webhook、定时任务、队列和 Agent 工具。内容涵盖节点错误输出、重试、输入验证和 HTTP 响应,并指出可能绕过错误分支的失败情形。

主要功能

  1. 01配置节点级错误输出路由
  2. 02设置工作流级兜底处理
  3. 03重试暂时性故障
  4. 04处理 Webhook 验证与状态码
  5. 05识别静默失败路径

适用场景

  1. 01为 Webhook API 返回结构化错误
  2. 02监控无人值守工作流故障
  3. 03区分无效输入与服务错误
  4. 04检查错误输出连线

安装

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

$npx skills add https://github.com/czlonkowski/n8n-skills --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。

错误输出永远捕获不到的失败

即使错误输出配置正确,仍会漏掉整类失败。在 n8n 2.38.5 上验证:

失败使用 continueErrorOutput使用 continueRegularOutput
{{ }} 内部的 JS 错误(缺失路径导致的 TypeError、对错误输入执行 JSON.parse、抛出的 Error、JMESPath 语法错误)不会触发任何输出;字段为 null,项目会走成功路径同左
Python Code 节点在运行前被拒绝(导入被拦截、dunder 访问:Security violations detected)或返回格式错误(逐项模式下返回列表)节点标记为失败,但未更改的输入项目会从成功输出离开;main[1] 仍为空,执行状态显示成功未更改的输入项目会直接传递
代码运行时发生异常(raise/throw、KeyError、NameError)路由到 main[1],格式为 { error } ✅主输出上出现 { error } 项目

因此,仅靠错误分支无法防护以下情况:

  • 检查数据,而不只是检查节点。 在输出结果很重要的转换节点之后,检查生成的字段是否存在(使用 IF/Filter 检查 {{ $json.total !== undefined && $json.total !== null }}),并自行将缺失情况发送到错误路径。
  • 测试运行并查看节点状态和输出值。 Python 示例显示,执行状态为绿色,但其中有红色节点。表达式示例则只显示 null。
  • 根本原因与修复方法:n8n-expression-syntax(调试)和 n8n-code-python(错误与 onError)。

优先实现自我修复:配置 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(或下方的逐字段验证器)对此进行分支处理,并直接返回 400/401/403/404。错误输出用于处理意外故障(5xx)。
  3. 即使在错误分支上,options.responseCode 的默认值也是 200。 这是一个隐蔽陷阱(参见 RESPONSE_SHAPES.md 和 n8n-node-configuration NODE_FAMILY_GOTCHAS.md):错误分支返回 200 和错误正文时,调用方的 HTTP 客户端会将其视为成功,因此不会触发错误处理。请在每个 Respond 节点上显式设置 responseCode。
输入验证:画布上的条件

将检查放在一个 IF 节点中:每条规则对应一个布尔条件(is true),组合方式为 and。为 true → 执行你的逻辑(200);为 false → 返回一个 400 响应,并使用静态的 validation_error 响应体回显预期的 schema,以便调用方自行修正。当调用方需要逐字段消息时(例如 LLM 客户端、公共 API),用一个 Code 节点替换 IF 节点,让它遍历 schema 列表并返回 { valid, validationError, details, requiredSchema },同时附上说明 Why Code: IF considered — per-field messages need a loop over the schema。这两种模式、约束速查表和转义规则都在 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 从错误输出中产生(“我们尝试了,但出错了”)。

**使用 Switch 按原因路由;每个状态码对应一个静态 Respond。**从错误中选择状态码属于路由工作。使用 Switch 根据错误消息进行分类,然后将每个输出连接到一个 Respond,并设置固定的 options.responseCode 和固定的响应体:

error outputs (main[1]) ─→ Switch "Classify error" (on $json.error.message)
                             ├─ contains "INVALID_ID"     → Respond 400 validation_error
                             ├─ contains "429"            → Respond 429 rate_limit_exceeded
                             ├─ contains "timeout"        → Respond 504 upstream_timeout
                             └─ fallback (extra output)   → Respond 500 internal_error

画布上会显示每个状态码,添加状态码也就是添加一条规则。将 Switch 的 options.fallbackOutput 设置为 extra,否则未匹配的错误会被丢弃,调用方将一直等待。只有两种结果时,可以在 Response Code 字段中使用一个简短的三元表达式,让单个 Respond 返回计算得出的状态码。Switch JSON 见 API_WORKFLOWS.md。

默认响应封装为 { "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 → Slack/email (post to #incidents; the alert text is built with expressions in the message field)

一条好的警报应包含工作流名称、指向编辑器和失败执行记录的链接、失败节点名称,以及真实的错误消息(而不是“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
错误分支返回 200 和 {error} 正文调用方的客户端会将其视为成功;错误处理不会触发在错误分支的 Respond 中明确将 options.responseCode 设置为 4xx/5xx
所有情况都返回一个 500 internal_error调用方无法分辨是输入有误还是服务中断根据原因映射状态码(调用方的问题用 4xx,你的问题用 5xx)
在 Code 节点中捕获错误并将其作为数据返回下游会处理错误格式的数据并继续运行让错误抛出;使用 onError: "continueErrorOutput" 并连接对应路径
网络节点未设置 retryOnFail每次短暂的 429/网络抖动都会引发 5xx;无关紧要的警报也会触发retryOnFail: true, maxTries: 3, waitBetweenTries: 5000
在 Respond 的 Response Code 或正文中编写函数体路由逻辑隐藏在字段中,运行时错误会变成静默的 null根据原因进行分支判断 → 每个状态码使用一个静态 Respond
无人值守的工作流没有错误工作流真正发生的故障无人处理构建 Error Trigger 工作流,并在 UI 中为其分配工作流
错误工作流通知与其监控的工作流使用同一个频道频道宕机 → 错误工作流也失败 → 错误消失使用不同的频道,并增加 Data Table 作为后备
在响应中泄露 $json.error(堆栈/SQL/令牌)向调用方/攻击者暴露内部信息私下记录日志,返回经过清理的消息

参考文件

文件阅读时机
NODE_ERROR_OUTPUTS.md为单个可能失败的节点连接节点级错误输出时
API_WORKFLOWS.md构建/审查 Webhook → Respond 工作流时,包括输入验证(IF 条件、逐字段 Code 验证器)
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 — $json.error.message 上的 Switch 条件和警报消息表达式依赖正确的 {{ }} 语法及对 $json.error 的访问。
  • n8n-code-javascript / n8n-code-python — 如果在 Code 节点内部捕获错误,请慎重决定:重新抛出以使用错误输出,或处理错误后继续运行。不要返回错误格式的数据,却假装执行成功。
  • n8n-code-tool — Agent 的 Code Tool 会将抛出的错误返回给大模型,然后由大模型重试;这与工作流节点的错误约定不同。
  • n8n-binary-and-data — 文件/二进制操作也可能失败;像连接任何网络节点一样连接其错误输出。

快速参考清单

对于 API / webhook 工作流:

  • Webhook 触发器使用 responseMode: "responseNode"
  • 上游验证输入 → 使用 4xx 响应(IF 条件,或逐字段 Code 验证器)
  • 每个可能失败的节点都设置 onError: "continueErrorOutput",并且连接了 main[1]
  • 网络节点设置 retryOnFail: true, maxTries: 3, waitBetweenTries: 5000
  • 错误路径以一个 Respond 节点结束,并在 options.responseCode 中明确设置 4xx/5xx(顶层的 responseCode 会被忽略)
  • 状态码与原因相符(调用方导致的问题用 4xx,你导致的问题用 5xx)
  • 错误正文为 { error, message },不含堆栈跟踪、SQL 或令牌
  • 使用 n8n_get_workflow 验证:每个可能失败的节点都同时包含 onError 和 main[1]

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

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

请记住:默认情况是静默。错误处理分两步——让故障流转(为每个节点设置 onError 并连接输出,或创建兜底错误工作流),再让它发声(通过状态码和正文如实说明情况)。只做一半比什么都不做更糟,因为看起来像是已经处理好了。

评论

支持 Markdown