Data Table Lookup triggers twice / Insert causes second evaluation of conditional branches

Hi everyone,

I’m currently testing the new Data Tables feature and ran into behaviour that looks unexpected — possibly a bug, or I may be misunderstanding how the execution model works with Data Tables.

What happens

I have a workflow that receives a webhook, runs input validation, and then performs two parallel checks:

  • If row exists –> update path

  • If row does not exist –> insert path

Both branches are fed directly from the same input node (InputValidation).

The issue

Even when the table is completely empty, the following happens:

  1. “Row does not exist” branch runs (correct)

  2. A new row is inserted (correct)

  3. After the insert, the “row exists” branch suddenly executes as well
    – even though it should only evaluate based on the original input item
    – and even though the table was empty at the beginning of the workflow

It looks as if the insert action causes a second evaluation of the other conditional branch, resulting in the workflow taking both branches during the same single webhook execution.

Expected behaviour

Only one of the two branches should run, based solely on the state of the Data Table at the time the input arrives.

Question

Is this expected (due to how items propagate in parallel branches), or is this a bug?
If this is expected, what is the recommended pattern to safely perform “exists / not exists” branching without race-like behaviour?

Thanks!

Hey @raving1522
This is expected behaviour in n8n - see https://www.youtube.com/watch?v=TFTLMQLozCI&t=27
If you want to properly branch, you’ll need to use the IF or Switch nodes.

{“nodes”: [{“parameters”: {},“type”: “n8n-nodes-base.manualTrigger”,“typeVersion”: 1,“position”: [16,-16],“id”: “41899947-ff74-40af-be33-65c552817617”,“name”: “When clicking ‘Execute workflow’”},{“parameters”: {“jsCode”: “// Loop over input items and add a new field called ‘myNewField’ to the JSON of each one\nfor (const item of $input.all()) {\n  item.json.myNewField = 1;\n}\n\nreturn $input.all();”},“type”: “n8n-nodes-base.code”,“typeVersion”: 2,“position”: [240,-16],“id”: “e57a2ed8-a279-460c-b30e-b2cef21c004e”,“name”: “InputValidation”},{“parameters”: {“operation”: “get”,“dataTableId”: {“__rl”: true,“value”: “fzIzaIHmqB12dV3q”,“mode”: “list”,“cachedResultName”: “subworkflow-ai”,“cachedResultUrl”: “/projects/vkJee1F2gd6CVCfI/datatables/fzIzaIHmqB12dV3q”},“filters”: {“conditions”: [{“keyValue”: “999”}]}},“type”: “n8n-nodes-base.dataTable”,“typeVersion”: 1,“position”: [464,-16],“id”: “7aa53ac2-5aec-41b1-a7e2-43d6bd2fb8cf”,“name”: “Get row(s)”,“alwaysOutputData”: true},{“parameters”: {“conditions”: {“options”: {“caseSensitive”: true,“leftValue”: “”,“typeValidation”: “strict”,“version”: 2},“conditions”: [{“id”: “c5a0b550-bd74-4863-93e1-6cc94b6e5ba3”,“leftValue”: “={{ $json }}”,“rightValue”: “”,“operator”: {“type”: “object”,“operation”: “notEmpty”,“singleValue”: true}}],“combinator”: “and”},“options”: {}},“type”: “n8n-nodes-base.if”,“typeVersion”: 2.2,“position”: [688,-16],“id”: “2920a554-3628-46ec-ae31-9749e96db14e”,“name”: “If Row Exists”},{“parameters”: {“operation”: “update”,“dataTableId”: {“__rl”: true,“value”: “fzIzaIHmqB12dV3q”,“mode”: “list”,“cachedResultName”: “subworkflow-ai”,“cachedResultUrl”: “/projects/vkJee1F2gd6CVCfI/datatables/fzIzaIHmqB12dV3q”},“columns”: {“mappingMode”: “defineBelow”,“value”: {},“matchingColumns”: ,“schema”: [{“id”: “file”,“displayName”: “file”,“required”: false,“defaultMatch”: false,“display”: true,“type”: “string”,“readOnly”: false,“removed”: false},{“id”: “datasetId”,“displayName”: “datasetId”,“required”: false,“defaultMatch”: false,“display”: true,“type”: “string”,“readOnly”: false,“removed”: false}],“attemptToConvertTypes”: false,“convertFieldsToString”: false},“options”: {}},“type”: “n8n-nodes-base.dataTable”,“typeVersion”: 1,“position”: [912,80],“id”: “31616bbf-ba88-4814-83b0-31df21d12804”,“name”: “Update row(s)”},{“parameters”: {“workflowId”: {“__rl”: true,“value”: “={{ $workflow.id }}”,“mode”: “id”,“cachedResultUrl”: “/workflow/=%7B%7B%20$workflow.id%20%7D%7D”},“workflowInputs”: {“mappingMode”: “defineBelow”,“value”: {},“matchingColumns”: ,“schema”: ,“attemptToConvertTypes”: false,“convertFieldsToString”: true},“options”: {}},“type”: “n8n-nodes-base.executeWorkflow”,“typeVersion”: 1.3,“position”: [912,-112],“id”: “bf6e595d-1c89-4396-9b59-6b4b61ec6439”,“name”: “Execute Subworkflow”},{“parameters”: {“content”: ““Always Output Data” set to “True” otherwise the execution will stop here if empty result.”,“height”: 96,“width”: 208,“color”: 7},“type”: “n8n-nodes-base.stickyNote”,“typeVersion”: 1,“position”: [416,128],“id”: “67174191-2d55-4d09-8437-843bc4923433”,“name”: “Sticky Note”}],“connections”: {“When clicking ‘Execute workflow’”: {“main”: [[{“node”: “InputValidation”,“type”: “main”,“index”: 0}]]},“InputValidation”: {“main”: [[{“node”: “Get row(s)”,“type”: “main”,“index”: 0}]]},“Get row(s)”: {“main”: [[{“node”: “If Row Exists”,“type”: “main”,“index”: 0}]]},“If Row Exists”: {“main”: [[{“node”: “Execute Subworkflow”,“type”: “main”,“index”: 0}],[{“node”: “Update row(s)”,“type”: “main”,“index”: 0}]]}},“pinData”: {},“meta”: {“instanceId”: “b9f144fdc910a1e14e522063b576e7e28af8b611858295f590957fc8454b2836”}}
1 Like

Thank you for your reply. Thats how I already worked around it. But good to know that this is the expected behaviour.

Here is the working example posted by Jim_Le, in case anyone else stumbles across the same problem.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.