n8n-error-handling について
n8nワークフローのエラー処理を設計するためのリファレンススキルです。特にWebhook、スケジュール実行、キュー、エージェントツールなどの無人実行を対象に、ノードのエラー出力、リトライ、入力検証、HTTP応答と、エラーブランチを通らない失敗を扱います。
主な機能
- 01ノード単位のエラー出力ルーティング
- 02ワークフロー全体の捕捉処理
- 03一時的な障害のリトライ
- 04Webhookの検証とステータスコード
- 05サイレントな失敗経路の検出
活用シーン
- 01Webhook APIで構造化エラーを返す
- 02無人ワークフローの障害を通知する
- 03不正な入力とサービスエラーを区別する
- 04エラー出力の配線を確認する
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ czlonkowski/ n8n-skills --skill n8n-error-handlingインストール後、Claude Code で/n8n-error-handlingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
API_WORKFLOWS.mdERROR_WORKFLOWS.mdNODE_ERROR_OUTPUTS.mdREADME.mdRESPONSE_SHAPES.md詳細
n8n エラー処理
デフォルトでは、n8n のノードでエラーが発生すると、ワークフロー全体が停止します。自分で実行を見守っている場合なら問題ありません。エラーになったノードが赤く表示されるので、修正できます。しかし、無人で動作するもの(Webhook API、cron ジョブ、キューワーカー、エージェントツールなど)では、このデフォルト設定は適切ではありません。呼び出し元にはタイムアウトか空の 500 が返され、運用担当者にはアラートが届きません。そして「ログも手がかりもないまま、インテグレーションが動かなくなった」という事態になります。
このスキルでは、失敗を明確で、構造化され、復旧可能な状態にする方法を説明します。さらに、理想的には自己修復できるようにして、一時的な障害を人が対処する前に解消します。
サイレントな失敗の大半を防ぐ、2 つの考え方は次のとおりです。
- ノード単位のエラー出力 — ノードの失敗を実行全体の停止につなげず、制御可能な 2 つ目の出力にルーティングします。
- ワークフローレベルのエラーワークフロー — ノード単位の処理をすり抜けた事象(タイムアウト、ノード間のクラッシュ、未接続の失敗など)をすべて捕捉します。
実際に必要となるケース
| ワークフローの形態 | エラー処理の方針 |
|---|---|
Webhook / API(Respond to Webhook を含むもの) | 必須です。 失敗する可能性のあるすべてのノードで、エラー出力を接続し、原因に応じたステータスコードを返します。 |
| スケジュール実行 / cron / キューワーカー / エージェントツール(無人実行) | 必須です。 ワークフローレベルのエラーワークフローを設定し、ネットワークノードに retryOnFail を設定します。 |
| 自分で実行して見守る、内部向けの単発ワークフロー | 任意です。 デフォルトの onError: "stopWorkflow" で問題ありません。エラーになったノードを確認して再実行できます。 |
判断基準は、出力をあなた以外の誰かが目にするかどうかです。下流システム、エンドユーザー、オンコールエンジニアなどが目にする場合、失敗を無視するのではなく処理する必要があります。自分だけが実行を見守り、失敗しても「気づいて再実行する」だけで済むなら、より緩やかな設定でも問題ありません。
最大のサイレントな落とし穴:ノード単位のエラー出力は 2 段階で設定する
これは、実際にはエラーを握りつぶしているのに、n8n のワークフローがエラーを「処理」しているように見える最も一般的な原因です。ノードの失敗をハンドラーにルーティングするには、2 つの変更が必要です。片方だけ行うと、設定が完了したように見えても正しく動作しません。
- ノードに
onError: "continueErrorOutput"を設定します。これによって 2 つ目の出力が作成されます。これを設定しないと、どのように接続してもmain[1]は存在しません。 - そのエラー出力(
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)、または不正な戻り値の形式(each-itemモードでリストを返す) | ノードは失敗と判定されますが、変更されていない入力アイテムは成功出力から出ていきます。main[1] は空のままで、実行は成功と表示されます | 変更されていない入力アイテムがそのまま通過します |
コードの実行中に発生する例外(raise/throw、KeyError、NameError) | { error } として main[1] にルーティングされます ✅ | メイン出力に { error } アイテムが出力されます |
そのため、エラーブランチだけでは以下の問題を防げません。
- ノードだけでなく、データを検証します。 出力が重要な変換処理の後で、生成したフィールドが存在するか確認し(
{{ $json.total !== undefined && $json.total !== null }}によるIF/Filter)、値がない場合は自分でエラーパスに送ります。 - テスト実行し、ノードのステータスと出力値を確認します。 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でトリガーされ、呼び出し元に応答するワークフローには、ほかのすべてに優先するルールが1つあります。分岐を未完了のままにしないことです。成功パスもすべてのエラーパスも、必ず 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
これを機能させるポイントは3つです。
- 複数の分岐を1つのエラー応答ノードに集約します。 エラーが起こりうる多くのノードから、
main[1]を単一のRespondノードにルーティングできます。グラフを読みやすく保てます。 - 検証エラー(4xx)は、エラー出力ではなく上流で確認します。 フィールドの欠落はノードのクラッシュではありません。既知のレスポンスを返す、想定された結果です。IF/Switch(または以下のフィールド単位のバリデーター)で分岐し、400/401/403/404を直接返します。エラー出力は予期しない失敗(5xx)に使用します。
options.responseCodeのデフォルト値は、エラーブランチでも200です。 これ自体が見落としやすい罠です(RESPONSE_SHAPES.mdおよび n8n-node-configuration のNODE_FAMILY_GOTCHAS.mdを参照)。エラーボディとともに200を返すエラーブランチは、呼び出し元のHTTPクライアントには成功に見えるため、エラー処理が実行されません。すべてのRespondノードでresponseCodeを明示的に設定してください。
入力検証:キャンバス上の条件
チェックを1つの IF ノードにまとめます。各ルールにつき1つのブール条件(is true)を設定し、結合方法は and にします。true の場合はロジック(200)へ、false の場合は、呼び出し元が自力で修正できるよう、期待されるスキーマを含む固定の validation_error 本文を返す 400 の Respond へ進みます。LLM クライアントや公開 API のように、呼び出し元がフィールドごとのメッセージを必要とする場合は、IF を、スキーマリストをループして { valid, validationError, details, requiredSchema } を返す Code ノード1つに置き換えます。その際の注記は 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 は「再試行しない」ことを示唆します。
よくある間違い: 不正な入力も含め、すべてを 500 internal_error を返す1つの Respond に接続してしまうことです。すると呼び出し元は自分のバグとこちらの障害を見分けられず、エラー率から実際のインシデントとクライアント側のノイズを区別できなくなります。
| 原因 | ステータス | error コード | 処理箇所 |
|---|---|---|---|
| 必須フィールドがない/型が誤っている | 400 | validation_error | 上流のチェック(IF 条件/フィールドごとのバリデーター)。エラー出力ではありません |
| 認証情報がない、または無効 | 401 | unauthorized | 上流のチェック |
| 認証済みだが許可されていない | 403 | forbidden | 上流のチェック |
| リクエスト内のリソース ID は有効だが、データに存在しない | 404 | not_found | ルックアップの結果で分岐します。エラーではありません |
| 現在の状態と競合する(重複、競合状態) | 409 | conflict | ロジックで検出 |
| 呼び出し元がレート制限を超過した | 429 | rate_limit_exceeded | Retry-After ヘッダーを設定 |
| ノードで例外が発生し、原因が不明 | 500 | internal_error | エラー出力パス |
| サードパーティ API がエラーを返した | 502 | upstream_error | HTTP ノードのエラー出力 |
| 現時点では処理できない(下流が停止中) | 503 | service_unavailable | 特定のエラーを検出し、再試行を促す |
| サードパーティ API がタイムアウトした | 504 | upstream_timeout | メッセージでフィルタリングしたエラー出力 |
したがって、フローは2種類に分かれます。4xx は処理を始める前に決定し(IF/Switch +専用の Respond)、5xx はエラー出力から返します(「試したが、失敗した」)。
Switch で原因ごとに振り分け、ステータスごとに固定の Respond を1つ用意します。 エラーからステータスコードを選ぶのは、ルーティングの役割です。エラーメッセージを Switch で分類し、各出力を、options.responseCode と本文が固定された Respond に接続します。
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
すべてのステータスがキャンバス上に表示され、追加する場合はルールを1つ追加するだけです。Switch の options.fallbackOutput は extra に設定してください。そうしないと、一致しないエラーが破棄され、呼び出し元が応答を待ち続けます。結果がちょうど2通りの場合は、Response Code フィールドに短い三項演算子を1つ指定し、計算したコードを返す単一の 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)
適切なアラートには、ワークフロー名、エディターへのリンク、失敗した実行へのリンク、失敗したノード名、そして実際のエラーメッセージ(「ワークフローに失敗しました」ではありません)を含めます。フィールド式と、オプションの「n8n ノード経由で失敗した入力を取得する」拡張については、ERROR_WORKFLOWS.mdをご覧ください。
あらかじめ注意しておきたい落とし穴が2つあります。
- 再帰の落とし穴。 エラーワークフローが Slack に通知し、Slack が停止していると、エラーワークフローも失敗し、元のエラーが消えてしまいます。監視対象のワークフローとは別のチャネルで通知してください(多くのワークフローが Slack に通知するなら、エラーワークフローはメールを使います)。また、通知に失敗しても記録が残るよう、フォールバック(Data Table への書き込み)を追加してください。
- 「処理済み」のエラーは上位に伝播しません。 ノードのエラー出力を、データを破棄するだけの何もしないノードにつなぐと、n8n はエラーを処理済みと見なし、エラーワークフローを実行しません。エラーを使って実際に何かを行う場合に限り、ノードごとにエラーを捕捉してください。
コミュニティ MCP ではできないこと: エラーワークフローの割り当て(インスタンスのデフォルトまたはワークフローごとの上書き)は、n8n のUI 設定です(Workflow Settings → Error Workflow)。これを設定する MCP ツールはありません。MCP でエラーワークフローを作成したら、ユーザーに正確な UI 操作手順を伝え、無人で実行する各ワークフローで同じ設定を繰り返すか、インスタンスのデフォルトを設定するよう案内してください。
コミュニティ MCP では利用できない機能
| やりたいこと | 実際のところ |
|---|---|
| ワークフローのエラーワークフロー設定を行う | UI のみ(Workflow Settings → Error Workflow)。MCP ツールはありません。ワークフローを作成してから、ユーザーに UI での操作手順を伝えてください。 |
| その他のワークフロー設定(Save Execution Data、タイムゾーン、タイムアウト、呼び出し元のポリシー)を切り替える | UI のみです。n8n_update_partial_workflow には updateSettings がありますが、エラーワークフローの割り当てが確実に公開されているわけではありません。UI で確認してください。 |
| インスタンス全体のエラーロギング(Sentry、サーバーログ)を有効にする | インスタンス設定が必要です。n8n ワークフローの範囲外です。 |
MCPでできること:エラーワークフローの作成、ノードの onError/retryOnFail の設定(updateNode/patchNodeField)、エラー出力の接続(sourceIndex: 1 を指定した addConnection)、検証(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 では、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 を 1 つずつ用意します |
| エラーワークフローのない無人運用のワークフロー | 実際のエラーがどこにも通知されません | Error Trigger ワークフローを作成し、UI で割り当てます |
| エラーワークフローが、監視対象のワークフローと同じチャネルに通知する | チャネルが停止するとエラーワークフローも失敗し、エラーが消えてしまいます | 別のチャネルと Data Table のフォールバックを使用します |
$json.error(スタックトレース/SQL/トークン)をレスポンスに漏らす | 呼び出し元や攻撃者に内部情報が露出します | ログには内部で記録し、サニタイズしたメッセージを返します |
参照ファイル
| ファイル | 読むタイミング |
|---|---|
| NODE_ERROR_OUTPUTS.md | 個々の失敗する可能性があるノードに、ノード単位のエラー出力を接続するとき |
| API_WORKFLOWS.md | 入力検証(IF 条件、フィールドごとの Code バリデーター)を含む、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 —
$json.error.messageに対する Switch 条件やアラートメッセージの式は、正しい{{ }}構文と$json.errorへのアクセスに依存します。 - n8n-code-javascript / n8n-code-python — Code ノード内でエラーを捕捉する場合は、エラー出力を使うために再スローするか、処理して続行するかを意識して選んでください。エラー形式のデータを返し、成功したように見せかけてはいけません。
- n8n-code-tool — エージェントの Code Tool がスローしたエラーは LLM に返され、LLM は再試行します。これはワークフローノードとは異なるエラー処理の契約です。
- n8n-binary-and-data — ファイル/バイナリ操作も失敗する可能性があります。他のネットワークノードと同じように、エラー出力を接続してください。
クイックリファレンスチェックリスト
API/Webhook ワークフローの場合:
- Webhook トリガーで
responseMode: "responseNode"を使用する - 入力を上流で検証する → 4xx を返す Respond(IF 条件、またはフィールドごとの Code バリデーター)
- 失敗する可能性のあるすべてのノードに
onError: "continueErrorOutput"を設定し、かつmain[1]を接続する - ネットワークノードに
retryOnFail: true, maxTries: 3, waitBetweenTries: 5000を設定する - エラーパスの終端は、明示的な 4xx/5xx の
options.responseCodeを設定した Respond にする(トップレベルのresponseCodeは無視されます) - 原因に合ったステータスコードにする(呼び出し元の原因なら 4xx、自分側の原因なら 5xx)
- エラーボディは
{ error, message }とする — スタックトレース、SQL、トークンは含めない -
n8n_get_workflowで確認し、失敗する可能性のある各ノードにonErrorとmain[1]があることを確かめる
無人(スケジュール/cron/キュー)ワークフローの場合:
- ネットワークノードに
retryOnFailを設定する - Error Trigger ワークフローを用意する(捕捉 → 通知、必要に応じて再試行)
- エラーワークフローは別のチャネルで通知し、フォールバックも備える(再帰の罠を防ぐ)
- n8n UI でエラーワークフローを設定する(MCP では設定できません — ユーザーに伝えてください)
覚えておいてください:デフォルトでは何も通知されません。エラー処理は 2 つの操作で行います。失敗をルーティングし(ノードごとの onError と接続済みの出力、または一括処理するエラーワークフロー)、それを通知します(事実を伝えるステータスコードとボディを返す)。片方だけでは、完了したように見えてしまうため、何もしないより悪い結果になります。