中級向け

n8n Execute Sub-workflowの使い方:宣言していない項目は黙って消える

ワークフローを分割して呼び出すノードです。once と each で子の実行数が変わること、入力を宣言すると宣言外の項目が届かないこと、each の直後に return $input.all() が落ちる理由を実際に動かして確かめました。

動作確認: — n8n 2.36.8 / Execute Sub-workflow ノード v1.3 / Execute Workflow Trigger v1.2

Execute Sub-workflow は、別のワークフローを呼び出すノードです。

同じ処理を何本ものワークフローに書いているとき、共通部分を切り出して1本にまとめられます。 通知の送り方、ログの残し方、データの整形。直したいときに1箇所で済むのが利点です。

ただし呼ぶ側と呼ばれる側でデータの受け渡し方に決まりがあり、ここを知らないと データが消えます。親子2本のワークフローを作って確かめました。

呼ぶ側と呼ばれる側

2つのノードが対になっています。

役割 ノード
呼ぶ側(親) Execute Sub-workflow
呼ばれる側(子) Execute Workflow Trigger

子の先頭に Execute Workflow Trigger を置くと、そのワークフローは「呼ばれる用」になります。

once と each で子の実行数が変わる

親のノードには Mode があります。

n8nのExecute Sub-workflowノード設定画面。Modeのドロップダウンが開いており、Run once with all items と Run once for each item の2つが説明文つきで並んでいる
Mode は2択。既定は Run once with all items です

3件のデータを流して、それぞれで試しました。

Mode 子が受け取る件数 子の実行回数
Run once with all items(既定) 3件まとめて 1回
Run once for each item 1件ずつ 3回

子は別の実行として記録される

子は親とは別の実行として履歴に残ります。実行データを見ると、 mode が integrated になっていました。

{ "id": "77", "workflowId": "BAySRuvDoyrhBRw0",
  "status": "success", "mode": "integrated" }

手動実行なら manual、Webhook なら webhook です。integrated は「他から呼ばれた」印で、 これで見分けられます。

each のときは、親に返ってくるアイテムに子の実行IDが付いていました。

"metadata": { "subExecution": { "executionId": "79", "workflowId": "BAySRuvDoyrhBRw0" } }

どのアイテムがどの子実行に対応するかを辿れます。

each の直後に return $input.all() を書くと落ちる

この metadata が、そのまま落とし穴になりました。

each で呼んだ直後に Code ノードを置き、return $input.all(); と書いたところ、 ワークフローが止まりました。

Invalid output format [item 0]
An output item contains the reserved key `json`.

対処は、json だけ取り出して返すことです。

// metadata を落として json だけ返す
return $input.all().map(i => ({ json: i.json }));

これで通ることを確認しました。

入力を宣言すると、宣言していない項目は消える

ここが一番はまるところです。

子の Execute Workflow Trigger には Input data mode という設定があり、 3つから選べます。

表記 何をするか
Define using fields below(既定) 受け取る項目を1つずつ宣言する
Define using JSON example 例のJSONから項目を推測させる
Accept all data 親から来たデータをすべてそのまま受け取る

親からは id / name / memo の3項目を送りました。 子で id と name だけを宣言した状態で実行した結果です。

// 子が受け取った内容
{ "id": 1, "name": "田中" }

memo が消えています。

全部そのまま渡したいなら Accept all data を選びます。 この設定で実行したときは、memo も含めて3項目とも届きました。

親のExecute Sub-workflowノードの設定画面。Workflow Inputs に id と name の2項目が式つきで並び、下に Attempt To Convert Types のトグル、Mode、Options、Wait For Sub-Workflow のトグルがある
子が宣言した項目が、親側に入力欄として出てきます

子で項目を宣言すると、親側にその項目の入力欄が出ます。ここに何を渡すかを式で書く形です。

待たない設定では、子の結果が返らない

Options に Wait For Sub-Workflow があり、既定はオンです。

これをオフにして実行したところ、親に返ってきたのは子の処理結果ではなく、 子へ渡す前の入力そのものでした。

// 待たない設定のとき、親に返ってきたもの
{ "id": 1, "name": "田中", "memo": "宣言しない項目" }

子が何を返したかに関係なく、入力がそのまま素通しされます。

使い分けの目安

  • 共通処理を切り出すのが主な用途。直す場所が1箇所になります
  • Mode は基本 once。 each は実行履歴が件数ぶん増えます
  • 項目を宣言するか、Accept all data にするかは最初に決める。 後から項目が増えたとき、 宣言方式だと子側の修正を忘れて消えます
  • each の直後の Code ノードでは $input.all() をそのまま返さない

検証していないこと

  • Source は Database(保存済みのワークフローをIDで指定)だけ試しました。 JSONを直接渡す方式やURLから読む方式は動かしていません
  • Define using JSON example は試していません。 宣言方式と Accept all data の2つだけです
  • 子でエラーが起きたときに親がどうなるかは確認していません。 これは別途確かめる価値があります
  • 子から親へ戻る値の形は、子の最後のノードの出力がそのまま返ることを確認しただけです。 複数ノードに分岐しているときの挙動は見ていません
  • 検証はすべて手動実行です
  • Input data mode の選択肢の表記は、画面ではなくノード定義の表示名から読み取りました。 Mode と Wait For Sub-Workflow は画面で確認しています

「親では入っているのに子で消える」 が、このノードで一番多い詰まり方だと思います。 原因は入力の宣言です。

次に読む