The n8n Merge node waits for every connected input to finish, then combines them. The setting that causes the most confusion is the default behaviour of Combine mode: items that do not find a match on the other side are dropped silently. If your workflow is losing records, that is almost certainly why.
Merge is one of the few nodes in n8n that changes the shape of your data rather than its content, which makes its failure modes quiet. Nothing errors. You just get fewer items out than you expected, and finding out why means understanding which mode you picked.
Table of contents
- What Merge does, and when it runs
- The modes, and what each is for
- The item-loss trap
- Matching on fields that are not quite equal
- Debugging a Merge that produces the wrong count
- Running n8n where the executions stick around
- How this fits the rest of the stack
- FAQ
What Merge does, and when it runs
A Merge node has two or more inputs. It waits for all of them before producing output, which makes it the natural join point after a branch — an If node, a Switch, or two independent API calls running in parallel.
That waiting behaviour matters. If one input never receives data because a branch did not execute, the Merge node does not run, and everything downstream of it does not run either. A workflow that stops silently at a Merge usually has an empty branch upstream.
Add inputs by increasing the Number of Inputs parameter. Inputs are numbered, and the numbering is meaningful for every mode except Append.
The modes, and what each is for
Append — output every item from input 1, then every item from input 2, and so on. No matching, no data loss, no relationship between the two sets. Use it when you want one combined list.
Combine — the mode with the real behaviour, and three sub-options:
- Matching Fields — a SQL-style join. You name a field on each side and items pair up where the values are equal.
- Position — item 1 pairs with item 1, item 2 with item 2. Positional, ignoring content entirely.
- All Possible Combinations — a cross join. Three items and four items produce twelve.
Choose Branch — output the data from one specified input and discard the rest. Useful purely as a synchronisation point: wait for both branches, then continue with one.
SQL — run an actual SQL query across the inputs as tables. Powerful and worth knowing about for anything a join cannot express cleanly.
The item-loss trap
This is the thing to internalise. In Combine mode with Matching Fields, an item with no match on the other side is dropped by default. n8n calls these unpaired items, and the default behaviour is to leave them out.
Input 1: 100 customers from the CRM
Input 2: 60 customers with orders in the database
Combine by Matching Fields on `email`
-> 60 items out.
40 customers vanished. No error. No warning.
The workflow reports success.
The fix is under Add Option → Include Any Unpaired Items, enabled. That turns an inner join into a full outer join and the 40 come back with their database fields empty.
Decide deliberately which join you want, the same way you would in SQL:
- Only records present in both → leave unpaired items off (inner join).
- Everything from both sides → turn unpaired items on (full outer join).
- Everything from one side, enriched where possible → unpaired items on, then filter downstream.
The Position mode has the same shape of problem with a different cause: if input 1 has ten items and input 2 has seven, you get seven, because there is nothing to pair the remaining three with.
Matching on fields that are not quite equal
Matching Fields compares values exactly. Real data rarely cooperates — casing differs, whitespace creeps in, one system stores IDs as strings and the other as numbers.
Normalise before the merge, in a Set or Code node on each branch:
// Code node, Run Once for All Items
return $input.all().map(item => ({
json: {
...item.json,
// one canonical key on both branches
match_key: String(item.json.email ?? '').trim().toLowerCase(),
},
}));
Then merge on match_key rather than on email. A type mismatch between "1042" and 1042 produces zero matches and looks exactly like a broken connection, so coercing to a string on both sides is worth doing routinely.
Merge also supports multiple matching fields, which is the answer when no single field is unique — matching on both customer_id and order_date rather than trying to build a composite key by hand.
Debugging a Merge that produces the wrong count
- Run each input branch on its own and note the item count. Merge cannot output what it never received.
- Check the input numbering. Input 1 and input 2 are not interchangeable in Combine or Choose Branch mode.
- Open the Merge node’s input tabs and compare the actual values of the matching field on each side. This is where the whitespace and type mismatches become visible.
- Toggle Include Any Unpaired Items. If the count jumps to what you expected, the join type was the problem.
- Switch temporarily to Append. If that shows all your items, the data is arriving fine and the matching logic is the issue.
Item counts are shown on each connection in the editor. Watching where the number drops localises the problem faster than reading node configuration.
For workflows running on a schedule rather than in the editor, the execution list keeps input and output data per node, so the same comparison works after the fact — provided your execution data retention is long enough to still have the run.
Running n8n where the executions stick around
That last point is a real operational constraint. n8n stores execution history in its database, and the default SQLite file is fine for a handful of workflows and poor for anything busy — it grows, it locks under concurrent writes, and it is easy to lose.
Postgres as the execution database is the standard fix, and it is the difference between having the data to debug a failed merge and having a workflow that failed sometime last week.
RunxBuild runs n8n as a managed tool with its own plan, custom domains, environment variables, autoscaling, and logs — with a managed Postgres alongside it, so execution history lives somewhere durable. n8n on a $6 Basic plan with a database beside it is the usual shape for a small team, and the database documentation covers connection limits and backups.
Set EXECUTIONS_DATA_PRUNE and a retention window deliberately. Keeping everything forever fills the disk; keeping nothing means debugging blind.
How this fits the rest of the stack
Merge waits for all inputs, then joins them by the rule you picked. If items are disappearing, you are in Combine mode with unpaired items excluded — turn that option on or normalise the matching field so the join actually matches. Watch the item counts on the connections; they localise the problem faster than anything else. If you are pricing self-hosted n8n with a real database behind it, the RunxBuild hosting calculator shows the tool plan and the database separately.
Useful related references:
- n8n AI Agent Node: What It Does and When a Plain Chain Is Better
- n8n + Qdrant: A Vector Search Node for Real Workflows
- n8n HTTP Request Node: The Auth and Error Playbook
- Node services on RunxBuild
FAQ
Why does the n8n Merge node lose items?
In Combine mode with Matching Fields, items without a match on the other input are dropped by default. Enable Add Option, then Include Any Unpaired Items to keep them — this turns an inner join into a full outer join.
What is the difference between Append and Combine in the Merge node?
Append concatenates all inputs into one list with no matching and no loss. Combine joins them by matching fields, position, or all possible combinations, and can drop unmatched items depending on the settings.
Why is my Merge node not executing at all?
It waits for every connected input to produce data. If one branch did not run — an If node sent everything the other way, for example — the Merge never fires and the rest of the workflow stops with it.
My matching field looks identical but nothing matches. Why?
Almost always a type or whitespace difference. The string “1042” does not equal the number 1042. Normalise both branches with a Code or Set node, trimming and lowercasing into a single match key, then merge on that.
Should I use SQLite or Postgres for self-hosted n8n?
Postgres for anything beyond experimentation. SQLite locks under concurrent writes, grows without much management, and is easy to lose — which also means losing the execution history you need to debug failures.