🛡️ Node-RED Catchノード ガイド

このガイドでは、Node-REDのCatchノードについて、初心者の方でも理解できるように詳しく説明します。

📚 1. Catchノードとは?

🤔 「Catch」って何?

Catchノードは、フロー内で発生したエラーをキャッチ(捕捉)するノードです。 日常生活で例えると、サーカスの安全ネットのようなものです。

🎪 サーカスの安全ネットに例えると:

📦 基本的な動作

Inject Function
(エラー発生)
Debug

↓ エラー発生

Catch Debug
(エラー情報)

Catchノードは、指定したノード(または全ノード)でエラーが発生したとき、 エラー情報を含むメッセージを出力します。 これにより、エラー発生時の通知、ログ記録、リカバリ処理などを実装できます。

エラー発生時
throw new Error("処理失敗!")
↓ Catch ↓
エラー情報を取得
msg.error.message = "処理失敗!"

⚙️ 2. Catchノードの3つのスコープ

全ノード すべてのノード

同じタブ(フロー)内のすべてのノードからエラーをキャッチ

scope: null (設定で「すべてのノード」を選択)

選択 選択したノード

指定したノードのみからエラーをキャッチ

scope: ["node-id-1", "node-id-2"] (設定でノードを選択)

未捕捉 同じタブ内で捕捉されていないエラー

他のCatchノードでキャッチされなかったエラーのみをキャッチ

uncaught: true (最後の砦として機能)

📋 設定項目一覧

設定項目 説明 デフォルト値
名前 ノードの表示名(任意) 空白
エラー取得元 監視対象の選択方法(全ノード/グループ内/選択したノード) 全ノード
Catchノードで処理済みのエラーを無視 他のCatchノードで既に処理されたエラーを無視する オフ

🎯 msg.errorオブジェクト

Catchノードは、エラー発生時にmsg.errorオブジェクトを追加して出力します。

msg.error = { message: "Error Occurred!", // エラーメッセージ source: { id: "7bab9134.35ad", // エラー発生ノードのID type: "function", // ノードの種類 name: "Error", // ノードの名前 count: 1 // エラー発生回数 } }

🎯 3. 実用的な使用パターン

📥 サンプルフローのインポート方法:

  1. 下のサンプルフローJSONをコピー
  2. Node-REDエディタで メニュー → 読み込み を選択
  3. JSONをペーストして「読み込み」をクリック

このサンプルフローには、以下で説明する全パターンの実例が含まれています。

📋 サンプルフロー(クリックで展開)

参照元:NodeREDエディター内サンプルフロー

[ { "id": "33ba530c5670b49e", "type": "tab", "label": "catch", "disabled": false, "info": "", "env": [] }, { "id": "d1073c30.cb321", "type": "inject", "z": "33ba530c5670b49e", "name": "", "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "Hello, World!", "payloadType": "str", "x": 290, "y": 200, "wires": [ [ "7bab9134.35ad" ] ] }, { "id": "f96f0ba7.cfe008", "type": "debug", "z": "33ba530c5670b49e", "name": "", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "x": 610, "y": 240, "wires": [] }, { "id": "3ea03953.25a1a6", "type": "comment", "z": "33ba530c5670b49e", "name": "Example: Catch Node", "info": "Catch node can catch error caused by specified nodes or all nodes in a flow. It receives input of target node.", "x": 220, "y": 100, "wires": [] }, { "id": "d6a15623.056d18", "type": "comment", "z": "33ba530c5670b49e", "name": "Catch error of a node", "info": "", "x": 260, "y": 160, "wires": [] }, { "id": "7e1c072c.bc1cc8", "type": "comment", "z": "33ba530c5670b49e", "name": "Catch error caused by function node", "info": "", "x": 540, "y": 280, "wires": [] }, { "id": "b45ca54e.eca208", "type": "catch", "z": "33ba530c5670b49e", "name": "", "scope": [ "7bab9134.35ad" ], "uncaught": false, "x": 460, "y": 240, "wires": [ [ "f96f0ba7.cfe008" ] ] }, { "id": "7bab9134.35ad", "type": "function", "z": "33ba530c5670b49e", "name": "Error", "func": "throw new Error(\"Error Occured!\")", "outputs": 0, "noerr": 0, "x": 450, "y": 200, "wires": [] } ]

パターン1: 特定ノードのエラーをキャッチ

用途: 特定のノード(Function等)で発生するエラーを個別に処理

Inject Function
(Error)


Catch
(選択)
Debug

📌 動作の流れ:

  1. Functionノードで throw new Error("Error!") を実行
  2. Catchノードがエラーを捕捉
  3. msg.errorにエラー情報が格納されて出力
  4. Debugノードでエラー内容を確認

設定例:

Catchノード設定: エラーをキャッチ: 選択したノード 対象: Functionノード「Error」を選択 Functionノードのコード: throw new Error("Error Occured!");

📌 応用パターン(サンプルフローには含まれていません):

Catchノードは以下のような高度なエラーハンドリングにも活用できます:

🏋️ 4. 実践演習

演習1: 基本的なエラーキャッチ初級

📝 課題:

Functionノードで意図的にエラーを発生させ、Catchノードでキャッチしてください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

Injectノードの設定:

  • payload: タイムスタンプ(デフォルト)でOK

Functionノードの設定:

  • 名前: 「エラー発生」
  • コード: throw new Error("テストエラー");

Catchノードの設定:

  • エラーをキャッチ: 選択したノード
  • 対象: Functionノード「エラー発生」を選択

Debugノードの設定:

  • 出力: msg全体(complete message object)
✅ 解答例フロー
[ { "id": "ex1_tab", "type": "tab", "label": "演習1", "disabled": false, "info": "" }, { "id": "ex1_inject", "type": "inject", "name": "テスト実行", "props": [ { "p": "payload" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 130, "y": 100, "wires": [ [ "ex1_function" ] ], "z": "ex1_tab" }, { "id": "ex1_function", "type": "function", "name": "エラー発生", "func": "throw new Error(\"テストエラー\");", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 300, "y": 100, "wires": [ [] ], "z": "ex1_tab" }, { "id": "ex1_catch", "type": "catch", "name": "エラーキャッチ", "scope": [ "ex1_function" ], "uncaught": false, "x": 310, "y": 160, "wires": [ [ "ex1_debug" ] ], "z": "ex1_tab" }, { "id": "ex1_debug", "type": "debug", "name": "エラー確認", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "x": 490, "y": 160, "wires": [], "z": "ex1_tab" } ]

演習2: エラー情報の整形中級

📝 課題:

エラー情報を整形して、わかりやすいログメッセージを作成してください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

Functionノードの設定:

  • 名前: 「計算処理」
  • コード: throw new Error("ゼロ除算エラー");

Catchノードの設定:

  • エラーをキャッチ: すべてのノード

Templateノードの設定:

  • 形式: mustache
  • テンプレート: [{{error.source.name}}] エラー: {{error.message}}
  • 出力: 文字列

Debugノードの設定:

  • 出力: msg.payload
✅ 解答例フロー
[ { "id": "ex2_tab", "type": "tab", "label": "演習2", "disabled": false, "info": "" }, { "id": "ex2_inject", "type": "inject", "name": "テスト実行", "props": [ { "p": "payload" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 130, "y": 100, "wires": [ [ "ex2_function" ] ], "z": "ex2_tab" }, { "id": "ex2_function", "type": "function", "name": "計算処理", "func": "throw new Error(\"ゼロ除算エラー\");", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 300, "y": 100, "wires": [ [] ], "z": "ex2_tab" }, { "id": "ex2_catch", "type": "catch", "name": "", "scope": null, "uncaught": false, "x": 280, "y": 160, "wires": [ [ "ex2_template" ] ], "z": "ex2_tab" }, { "id": "ex2_template", "type": "template", "name": "エラー整形", "field": "payload", "fieldType": "msg", "format": "text", "syntax": "mustache", "template": "[{{error.source.name}}] エラー: {{error.message}}", "output": "str", "x": 440, "y": 160, "wires": [ [ "ex2_debug" ] ], "z": "ex2_tab" }, { "id": "ex2_debug", "type": "debug", "name": "結果", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 590, "y": 160, "wires": [], "z": "ex2_tab" } ]

演習3: 複数Catchの使い分け中級

📝 課題:

特定ノード用Catchと未捕捉エラー用Catchを組み合わせて使ってください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

Inject Aノードの設定:

  • 名前: 「処理A実行」
  • 接続先: 処理Aノード

Inject Bノードの設定:

  • 名前: 「処理B実行」
  • 接続先: 処理Bノード

Functionノード(処理A)の設定:

  • 名前: 「処理A」
  • コード: throw new Error("処理Aでエラー");

Functionノード(処理B)の設定:

  • 名前: 「処理B」
  • コード: throw new Error("処理Bでエラー");

Catchノード(専用)の設定:

  • 名前: 「専用キャッチ」
  • エラーをキャッチ: 選択したノード
  • 対象: 処理Aノードを選択

Catchノード(未捕捉)の設定:

  • 名前: 「未捕捉キャッチ」
  • エラーをキャッチ: すべてのノード
  • 未捕捉のみ: オン
✅ 解答例フロー
[ { "id": "ex3_tab", "type": "tab", "label": "演習3", "disabled": false, "info": "" }, { "id": "ex3_inject_a", "type": "inject", "name": "処理A実行", "props": [ { "p": "payload" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 130, "y": 80, "wires": [ [ "ex3_func_a" ] ], "z": "ex3_tab" }, { "id": "ex3_func_a", "type": "function", "name": "処理A", "func": "throw new Error(\"処理Aでエラー\");", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 80, "wires": [ [] ], "z": "ex3_tab" }, { "id": "ex3_inject_b", "type": "inject", "name": "処理B実行", "props": [ { "p": "payload" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 130, "y": 140, "wires": [ [ "ex3_func_b" ] ], "z": "ex3_tab" }, { "id": "ex3_func_b", "type": "function", "name": "処理B", "func": "throw new Error(\"処理Bでエラー\");", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 290, "y": 140, "wires": [ [] ], "z": "ex3_tab" }, { "id": "ex3_catch_a", "type": "catch", "name": "専用キャッチ", "scope": [ "ex3_func_a" ], "uncaught": false, "x": 310, "y": 200, "wires": [ [ "ex3_debug_a" ] ], "z": "ex3_tab" }, { "id": "ex3_debug_a", "type": "debug", "name": "処理A専用", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "error", "targetType": "msg", "x": 490, "y": 200, "wires": [], "z": "ex3_tab" }, { "id": "ex3_catch_uncaught", "type": "catch", "name": "未捕捉キャッチ", "scope": null, "uncaught": true, "x": 320, "y": 260, "wires": [ [ "ex3_debug_uncaught" ] ], "z": "ex3_tab" }, { "id": "ex3_debug_uncaught", "type": "debug", "name": "その他エラー", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "error", "targetType": "msg", "x": 510, "y": 260, "wires": [], "z": "ex3_tab" } ]

演習4: エラーログ記録システム上級

📝 課題:

エラー発生時にログをファイルに記録し、一定回数以上のエラーで警告を出すシステムを作成してください。

🎯 要求仕様:

📊 期待される動作:

✅ 成功の条件:

💡 ヒント

エラー発生用Functionノードの設定:

  • 名前: 「データ処理」
  • コード: throw new Error("データ形式エラー");

Catchノードの設定:

  • エラーをキャッチ: すべてのノード

ログ整形用Functionノードの設定:

  • 名前: 「ログ整形」
  • flow変数でエラーカウントを管理
  • 日時の整形には new Date().toISOString() を使用
  • カウントが3以上で警告メッセージを追加

Functionノードのコード例:

// エラーカウントを取得・更新 let count = flow.get("errorCount") || 0; count++; flow.set("errorCount", count); // タイムスタンプ作成 let timestamp = new Date().toLocaleString('ja-JP'); // ログメッセージ作成 let log = timestamp + " [" + msg.error.source.name + "] "; log += "エラー: " + msg.error.message; log += " (発生回数: " + count + ")"; // 3回以上で警告追加 if (count >= 3) { log = "⚠️ 警告: エラーが頻発しています!\n" + log; } msg.payload = log; return msg;
✅ 解答例フロー
[ { "id": "ex4_tab", "type": "tab", "label": "演習4", "disabled": false, "info": "" }, { "id": "ex4_inject", "type": "inject", "name": "テスト実行", "props": [ { "p": "payload" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 130, "y": 100, "wires": [ [ "ex4_error_func" ] ], "z": "ex4_tab" }, { "id": "ex4_error_func", "type": "function", "name": "データ処理", "func": "throw new Error(\"データ形式エラー\");", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 300, "y": 100, "wires": [ [] ], "z": "ex4_tab" }, { "id": "ex4_catch", "type": "catch", "name": "", "scope": null, "uncaught": false, "x": 280, "y": 160, "wires": [ [ "ex4_log_func" ] ], "z": "ex4_tab" }, { "id": "ex4_log_func", "type": "function", "name": "ログ整形", "func": "// エラーカウントを取得・更新\nlet count = flow.get(\"errorCount\") || 0;\ncount++;\nflow.set(\"errorCount\", count);\n\n// タイムスタンプ作成\nlet timestamp = new Date().toLocaleString('ja-JP');\n\n// ログメッセージ作成\nlet log = timestamp + \" [\" + msg.error.source.name + \"] \";\nlog += \"エラー: \" + msg.error.message;\nlog += \" (発生回数: \" + count + \")\";\n\n// 3回以上で警告追加\nif (count >= 3) {\n log = \"⚠️ 警告: エラーが頻発しています!\\n\" + log;\n}\n\nmsg.payload = log;\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 440, "y": 160, "wires": [ [ "ex4_debug" ] ], "z": "ex4_tab" }, { "id": "ex4_debug", "type": "debug", "name": "エラーログ", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 610, "y": 160, "wires": [], "z": "ex4_tab" }, { "id": "ex4_reset_inject", "type": "inject", "name": "カウントリセット", "props": [ { "p": "payload" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 140, "y": 220, "wires": [ [ "ex4_reset_func" ] ], "z": "ex4_tab" }, { "id": "ex4_reset_func", "type": "function", "name": "リセット", "func": "flow.set(\"errorCount\", 0);\nmsg.payload = \"エラーカウントをリセットしました\";\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 300, "y": 220, "wires": [ [ "ex4_debug" ] ], "z": "ex4_tab" } ]

🎓 5. まとめ

Catchノードの重要ポイント

⚠️ よくある間違い

📚 次のステップ

Catchノードをマスターしたら、以下のノードも学んでみましょう:

🔧 6. トラブルシューティング

よくある問題と解決方法

問題 原因 解決方法
エラーがキャッチされない Catchノードが別タブにある エラー発生ノードと同じタブにCatchを配置
特定ノードのエラーのみキャッチしたい スコープが「すべてのノード」になっている 「選択したノード」に変更して対象を指定
Catchが2回発火する 複数のCatchが同じノードを対象にしている スコープを見直して重複を解消
msg.errorが空 エラーがthrow以外の方法で発生 node.error(msg, "エラー")を使用して明示的にエラー発生
未捕捉Catchが動作しない 別のCatchがすでにキャッチしている 他のCatchのスコープを確認、または削除
エラー発生元がわからない msg.error.sourceを確認していない Debugでmsg全体を出力してsource.nameを確認

💡 7. 実務での活用例

ケース1: IoTセンサー異常検知

温度センサー/湿度センサー/気圧センサー ↓ データ変換処理(JSONパース) ↓ エラー発生時 Catchノード ↓ エラー情報を整形 ↓ 異常センサーを特定してダッシュボードに警告表示 + メンテナンス担当にSlack通知

ケース2: API連携エラーハンドリング

外部API呼び出し(天気API、決済API等) ↓ 通信エラー/タイムアウト Catchノード ↓ Functionノードでリトライ判定 ↓ リトライ可能 遅延後に再試行(最大3回) ↓ リトライ上限 代替処理(キャッシュデータ使用)+ エラーログ記録

ケース3: Raspberry Piデバイス監視

GPIOピン読み取り ↓ デバイス未接続/読み取りエラー Catchノード(デバイスごとに個別設定) ↓ エラー種別で分岐(Switch) ├─ 接続エラー → 再接続試行 ├─ 読み取りエラー → センサーリセット └─ その他 → 管理者通知 未捕捉Catchノード(フォールバック) ↓ 全エラーログをファイル保存

ケース4: データベース操作の堅牢化

MySQLノード(データ挿入/更新) ↓ 接続エラー/クエリエラー Catchノード ↓ エラー種別を判定 ├─ 接続エラー → 接続プール再作成 ├─ 重複キー → 既存データ更新に切替 ├─ タイムアウト → トランザクションロールバック └─ その他 → エラーログ + 管理者通知 成功時はStatusノードで監視

📖 8. node.error() と throw の違い

Functionノードでのエラー発生方法

方法 構文 特徴
throw throw new Error("メッセージ"); 処理を即座に中断、Catchでキャッチ可能
node.error() node.error("メッセージ", msg); エラーを発生させつつ処理を継続可能、第2引数でmsgを渡す
node.warn() node.warn("メッセージ"); 警告ログのみ、Catchでは捕捉されない
// throw: 処理中断 + Catchで捕捉 if (msg.payload === undefined) { throw new Error("payloadがありません"); } // node.error(): エラー発生 + 処理継続可能 if (msg.payload.value < 0) { node.error("負の値は無効です", msg); // ここで処理を継続するか、return null;で終了 } // node.warn(): 警告のみ(Catchでは捕捉されない) if (msg.payload.value > 100) { node.warn("値が上限を超えています: " + msg.payload.value); }

🔗 9. 追加リソース


このガイドが役に立ちましたら、実際のプロジェクトで練習してみてください!
Catchノードは堅牢なフロー構築に欠かせないツールです。

参照元:NodeREDエディター内サンプルフロー

🏠