bbizinets.life
n8nのMergeノードでデータが取れない原因と直接参照で解決した方法
実践ログ

n8nのMergeノードでデータが取れない原因と直接参照で解決した方法

n8nでGoogle Driveから複数ファイルを並列取得してMergeノードで統合したのに、後続のCodeノードで $input.all() を使ってもデータが揃わない、という問題に何度もハマりました。最初はファイル取得の失敗やJSONのパースエラーを疑いましたが、原因はMergeノードのAppendモードでRunが複数に分かれていたことでした。

この記事でわかること:

  • n8nのMergeノード(Appendモード)でRunが分割される仕組み
  • $input.all() で全データが取れない理由
  • $('ノード名').first() で直接参照して安定させる方法
  • 直接参照が向いているケース・向いていないケース
  • ノード名依存のリスクと命名ルールの考え方

MergeノードのあとにCodeノードでデータが揃わなかった

詰まった状況はこんな感じでした。

フロー1の記事生成前半で、Google Driveから複数の管理ファイルを取得していました。それぞれのノードが並列で動き、最後にMergeノード(Appendモード)でまとめてから、後続のCodeノードで各ファイルの内容を使って処理を進める設計でした。

ところが、Codeノードで $input.all() を使って全ファイルの内容を取ろうとすると、「あるはずのデータ」が取れなかったり、一部がnullになったりしました。

「ファイル取得に失敗しているのかな?」と思って各ノードの出力を確認すると、ファイル取得自体は成功しています。JSONも正しくパースされています。それなのに、Merge後のCodeノードでは揃わない。原因が分からず、しばらく迷子になりました。

今回の構成:並列4本でGoogle Driveを取得してMergeしていた

問題が起きた構成はこうでした。

記事自動生成フローの前半で、以下のファイルをGoogle Driveからそれぞれ別のノードで取得していました。

  • internal_links.csv(内部リンク候補)
  • title-rules(タイトルルール)
  • _index.json(投稿済み記事のインデックス)
  • keyword_done.csv(記事化完了キーワード)

これらは並列で動くノード構成で、それぞれが独立して取得処理を実行します。その結果をMergeノードのAppendモードで1つにまとめ、後続のCodeノードに渡す設計でした。

後続のCodeノードでは、これらの情報を使ってslug生成や記事設計の処理へ進む予定でした。ノードをつないだ段階では「Mergeで全部まとめれば大丈夫」と思っていました。

原因はAppendモードでRunが複数に分かれていたこと

原因に気づいたのは、MergeノードのOutput画面をよく見たときでした。

画面上部に「Run 1 of 2」や「Run 2 of 2」のような表示が出ていました。「Run? 2つある?」と思って調べて、ようやく仕組みが分かりました。

n8nでは、ノードの実行結果が「Run」という単位で分かれることがあります。少なくとも私の構成では、MergeノードのOutputが「Run 1 of 2」「Run 2 of 2」のように分かれていました。バージョンや構成によって挙動が変わる可能性もありますが、この記事では私の環境で起きた実践ログとして記録します。

Merge(Appendモード)の出力:
  Run 1 of 2 → internal_links.csv の内容
  Run 2 of 2 → _index.json の内容

そして、後続のCodeノードで $input.all() を使うと、「現在のRunに入っているアイテム」だけが対象になります。別のRunにあるデータは見えません。

つまり、$input.all() は「そのCodeノードに渡ってきている入力の全アイテム」であって、「そのワークフローの全ノードの全データ」ではありません。Mergeの結果がRunで分かれていると、1回の実行に全アイテムが揃っていない状態になります。

`$input.all()`で全データが取れると思っていた誤解

n8nのCodeノードを使い始めたとき、$input.all() を使えば前段の全アイテムを取れると思っていました。これは半分正解で半分誤解でした。

正確には、$input.all() は「現在の入力(current input)にある全アイテム」を返します。Mergeの出力が1つのRunにまとまっていれば全部取れますが、Runが分割されているとRunごとに見える範囲が異なります。

n8nでは「現在の入力」と「特定ノードの出力への参照」は別の概念です。

  • $input.all() → 現在のCodeノードに渡ってきているアイテム(Run依存)
  • $('ノード名').all() → 指定したノードの全実行結果(Run分割を超えて参照できる場合がある。ただし複数Run・複数アイテムを扱う場合の挙動は構成に依存するため注意が必要)
  • $('ノード名').first() → 指定したノードの最初の実行結果(この記事では1ファイル1アイテムの管理ファイル取得に限定して使用)

この違いを理解してから、設計の考え方が変わりました。

解決策:`$('ノード名').first()`で必要なノードを直接参照する

解決策はシンプルでした。Merge結果に依存するのをやめて、必要なノードを名前で直接参照することにしました。

const internalLinks = $('internal_links.csv取得').first().json;
const titleRules = $('title-rules取得').first().json;
const index = $('_index.json取得').first().json;
const keywordDone = $('keyword_done.csv取得').first().json;

return [{
  json: {
    internalLinks,
    titleRules,
    index,
    keywordDone
  }
}];

$('ノード名').first() は、指定したノードの最初の実行結果を直接取りに行きます。MergeノードのOutputがRunで分かれていても、各取得ノードの結果は独立しているため、この参照方法であれば安定して取得できました。

「Mergeで全部まとめてからCodeで処理する」という発想から、「Codeノードで必要なノードを明示的に読む」という設計に切り替えた形です。

直接参照に変えて安定した理由

$('ノード名').first() を使うと、Mergeノードの出力状態に左右されなくなります。

$input.all() はそのCodeノードに渡ってきている入力に依存します。MergeのOutput Runが分かれていれば、見えるアイテムの範囲が変わります。

一方、$('ノード名').first() は指定したノードを直接参照します。そのノードが1つのアイテムを返すファイル取得ノードであれば、.first() で確実にその1件を取れます。

特に「1ファイル1アイテム」の構成、つまりGoogle Driveから設定ファイルやCSVを1件ずつ取得するようなノードと相性が良いです。1記事分の管理ファイルを扱うような処理では、直接参照の方がシンプルで安定していると感じています。

CombineモードではなくAppendモードを使い続けた理由

「Combineモードに変えればよいのでは?」と思う方もいるかもしれません。

Combineモードはデータを横方向に結合する用途で使えます。ただし、結合の設定が複雑になりやすく、各ファイルのデータ構造が揃っていないと扱いにくい面があります。

今回は、各ファイルの内容を後続Codeノードで個別に扱いたかったため、Appendモードで集約しつつ、実際の値取得は直接参照にする方針にしました。Combineモードを否定するわけではなく、この用途では「直接参照した方がシンプルだった」という判断です。

同じ問題が複数のMergeノードで繰り返し起きた

最初の1回だけでなく、フローを拡張していく中で同じ問題が複数箇所で起きました。Google Drive取得に限らず、複数ノードをMergeした後のCode処理では同じ注意が必要です。

フロー2の後半でLLMの監査結果をMergeした場面でも発生しましたが、「データがない」のではなく「見ているRunに存在しない」だけだと分かっていれば、原因特定は早くなります。

この問題はn8n Webhook後にデータが消える問題をsession.jsonで解決した記事で書いたフロー構成とも関連しています。フローが複雑になるほど、Merge後の入力を信頼しすぎると原因特定が難しくなります。この経験から、LLMノードの後には必ずCodeノードを挟み、必要なノードを明示的に参照する方針にしています。

直接参照が向いているケース・向いていないケース

$('ノード名').first() は便利ですが、万能ではありません。判断基準として整理しておきます。

向いているケース:

  • 各ノードが1アイテムだけ返す(Google DriveのCSV・JSONファイル取得など)
  • 後続Codeノードで特定ノードの出力を確実に使いたい
  • Merge後のRun分割に左右されたくない
  • 管理ファイルや設定ファイルを明示的に参照したい

向いていないケース:

  • 複数アイテムを順番に処理する必要がある
  • Split in Batchesなどで大量アイテムを扱う
  • 現在の入力アイテムごとの対応関係が重要な処理
  • ノード名の変更が頻繁に起きる運用
  • 汎用的なサブワークフローとして再利用したい

1アイテムしか返さないノードへの参照と、複数アイテムを順番に処理するループとでは、適切な参照方法が異なります。用途に応じて使い分けることが大切です。

注意点:ノード名変更に弱い

$('ノード名') を使う設計には一つ注意点があります。ノード名に依存するため、後でノード名を変更すると参照が壊れます。

たとえば $('internal_links.csv取得') と書いたノードを後で $('内部リンク取得') に変えると、Codeノード側が参照できなくなってエラーになります。

対策として、以下を決めておくと安全です。

  • ノード名は分かりやすく、変更しにくい名前に固定する
  • ファイル名を含める命名規則にする(例:_index.json取得、keyword_done.csv取得)
  • 直接参照を使っているCodeノードには、どのノードを参照しているかコメントを残す

今後フローを引き継いだり、自分で数ヶ月後に見返したりしたときのために、命名ルールと参照箇所を揃えておくことをおすすめします。

まとめ

MergeノードのAppendモードを使うと、Runが複数に分かれることがあります。その状態で $input.all() を使っても、期待した全アイテムが揃わない場合があります。

解決策は、必要なノードを $('ノード名').first() で直接参照することでした。MergeのRun分割に左右されず、各ファイル取得ノードの結果を確実に取得できるようになりました。

まとめると:

  • $input.all() は現在のRunの入力範囲に限定される
  • AppendモードのMergeはRunが複数に分かれる場合がある
  • $('ノード名').first() で直接参照すると安定する
  • ただし、ノード名変更で壊れるため命名ルールの整備が必要

今回の問題は、Mergeノードの使い方だけでなく、後続処理が必要な情報をどこから読むかを設計する問題でもありました。こうした自動化の判断軸はbizinets.biz側で整理しています。

bizinets.bizの判断軸ページはこちら

bizinets.biz

記事を読んで「もっと知りたい」と思ったあなたへ

bizinets.biz では、ビジネスの仕組みづくりを体系的に学べるコンテンツを用意しています。まずは一度、覗いてみてください。

bizinets.biz へ行く →

関連記事