n8n-io / n8n

Free and source-available fair-code licensed workflow automation tool. Easily automate tasks across different services.
https://n8n.io
Other
42.84k stars 5.6k forks source link

Flaky Flows #7509

Open jpardoe86 opened 9 months ago

jpardoe86 commented 9 months ago

Describe the bug I have a workflow which requires me to reload the site every time before getting data, otherwise no data is returned.

Here’s what it starts as: image

When I click on “Execute previous nodes” after loading for the first time, it does this: image

So far, so good. But this is what happens if I click Execute Node: image

If I refresh the page and rather than clicking “Execute previous nodes” I straight away click “Execute node”, it works: image

The bug appears to be new as I’ve been fine up until this point, but it is seriously hindering productivity having to save then reload the page EVERY_SINGLE_TIME I need to test a node.

One clue as to what’s causing the bug might be the fact that I have a couple of IF nodes. I have noticed unpredictable behaviour where the green ticks go off down the wrong route. For example, if the result is TRUE, I see the green ticks actually go down the FALSE path instead. This may be why there is no data arriving at the node I’m working on. The IF node only sends data down the right path on first-load.

Here's a video of that behaviour (I checked and confirmed the IF node returned FALSE but you can see the process going down the TRUE path and then stalling): https://github.com/n8n-io/n8n/assets/134935050/868e7bcb-b9ee-42d4-95ea-0ca924ff80ae

Here’s the workflow: { "meta": { "instanceId": "0ee4df6913afa587809e4b8ad9c86ad691f8895b988194c29532314dcc84aaa4" }, "nodes": [ { "parameters": { "resource": "contact", "organizationId": "f053921e-ebcf-46f5-800d-659684ecd2e7", "name": "={{ $('Extract Contacts Domain').item.json['what is your legal company name?'] }}", "additionalFields": { "accountNumber": "={{ $json.id }}", "addressesUi": { "addressesValues": [ { "type": "STREET", "line1": "={{ $('Parse Response').item.json.address['Line 1'] }}", "line2": "={{ $('Parse Response').item.json.address['Line 2'] }}", "city": "={{ $('Parse Response').item.json.address.City }}", "region": "={{ $('Parse Response').item.json.address.Region }}", "postalCode": "={{ $('Parse Response').item.json.address['Postal Code'] }}", "country": "={{ $('Parse Response').item.json.address.Country }}" } ] }, "contactNumber": "={{ $('Extract Contacts Domain').item.json['what is your company\\'s registered phone number?'] }}", "emailAddress": "={{ $('Parse Response').item.json.email }}", "firstName": "={{ $('Parse Response').item.json.firstName }}", "lastName": "={{ $('Parse Response').item.json.lastName }}", "taxNumber": "={{ $('Extract Contacts Domain').item.json['please enter your TRN/TIN'] }}" } }, "id": "0188f448-819c-4cbd-83d9-8c563b7fef41", "name": "Xero", "type": "n8n-nodes-base.xero", "typeVersion": 1, "position": [ 1460, 1120 ], "credentials": { "xeroOAuth2Api": { "id": "zOmwinUszlev31Ks", "name": "Xero account" } } }, { "parameters": { "authentication": "oAuth2", "formId": "vD4eNvsj" }, "id": "de64f0e1-f2e9-4cf6-ad56-a5c9e2f71376", "name": "Typeform Trigger", "type": "n8n-nodes-base.typeformTrigger", "typeVersion": 1.1, "position": [ 540, 580 ], "webhookId": "50147da2-cb72-453a-87cd-6cda71004197", "credentials": { "typeformOAuth2Api": { "id": "NUGxx0iTZdnQ9KVr", "name": "Typeform account" } } }, { "parameters": { "tableId": "grow_clients_companies", "fieldsUi": { "fieldValues": [ { "fieldId": "legal_company_name", "fieldValue": "={{ $('Extract Contacts Domain').item.json['what is your legal company name?'] }}" }, { "fieldId": "domain", "fieldValue": "={{ $('Extract Contacts Domain').item.json.domain }}" } ] } }, "id": "b5b155de-1896-418a-b846-378c18241176", "name": "Supabase", "type": "n8n-nodes-base.supabase", "typeVersion": 1, "position": [ 2060, 920 ], "credentials": { "supabaseApi": { "id": "qKgUWvb7Qk0ikp0s", "name": "Supabase account" } } }, { "parameters": { "resource": "chat", "prompt": { "messages": [ { "content": "=Dissect the following address into Line 1, Line 2, City, Region, Postal Code and Country, then return JSON ONLY in a way that validates against the following JSON schema:\n{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"type\": \"object\",\n \"properties\": {\n \"Line 1\": {\n \"type\": \"string\"\n },\n \"Line 2\": {\n \"type\": \"string\"\n },\n \"City\": {\n \"type\": \"string\"\n },\n \"Region\": {\n \"type\": \"string\"\n },\n \"Postal Code\": {\n \"type\": \"string\"\n },\n \"Country\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\"City\", \"Country\"]\n}\n\nAddress: {{ $input.all()[0].json['what is your billing address?'] }}" } ] }, "options": {} }, "id": "3fa27334-82c2-4cf9-a0fb-286612987540", "name": "Dissect Address", "type": "n8n-nodes-base.openAi", "typeVersion": 1, "position": [ 1660, 920 ], "credentials": { "openAiApi": { "id": "15k7VlACBlhT15Dv", "name": "OpenAi account" } } }, { "parameters": { "jsCode": "const address = JSON.parse($('Dissect Address').all()[0].json.message.content);\nconst name = $('Typeform Trigger1').all()[0].json[\"who should we send invoices to?\"].split(\" \");\nconst firstName = name[0];\nconst lastName = name[1];\nconst email = $('Typeform Trigger1').all()[0].json[\"what is their accounting email?\"];\n\nreturn {\n address: address,\n firstName: firstName,\n lastName: lastName,\n email: email\n}" }, "id": "4fcee876-224d-438b-bd68-2af7d6830d5d", "name": "Parse Response", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1860, 920 ] }, { "parameters": { "resource": "contact", "operation": "getAll", "organizationId": "f053921e-ebcf-46f5-800d-659684ecd2e7", "options": { "includeArchived": false, "where": "=name=\"{{ $json['what is your legal company name?'] }}\"" } }, "id": "8607ce8f-807b-4c7a-903d-816df160b0cf", "name": "Check if Customer Exists", "type": "n8n-nodes-base.xero", "typeVersion": 1, "position": [ 740, 580 ], "alwaysOutputData": true, "credentials": { "xeroOAuth2Api": { "id": "zOmwinUszlev31Ks", "name": "Xero account" } } }, { "parameters": { "conditions": { "string": [ { "value1": "={{ $json.ContactID }}", "operation": "isNotEmpty" } ] } }, "id": "f755a077-cb42-4ba0-9ce3-3473a935a272", "name": "Does Customer Exist?", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [ 940, 580 ] }, { "parameters": { "resource": "contact", "operation": "getAll", "organizationId": "f053921e-ebcf-46f5-800d-659684ecd2e7", "options": { "where": "=isCustomer=true" } }, "id": "61597123-9b0e-42a1-be70-834a4dc1bca3", "name": "Check if Customer Exists1", "type": "n8n-nodes-base.xero", "typeVersion": 1, "position": [ 1140, 680 ], "alwaysOutputData": true, "credentials": { "xeroOAuth2Api": { "id": "zOmwinUszlev31Ks", "name": "Xero account" } } }, { "parameters": { "jsCode": "const items = $input.all();\nconst names = items.map((item) => item.json.Name);\nconst namesString = names.join(\", \");\nconst companyName = $('Typeform Trigger1').item.json[\"what is your legal company name?\"];\nreturn { companyName: companyName, names: namesString };\n" }, "id": "22335d83-f077-4fa0-81d5-b1c5304a6b8a", "name": "Get List of Existing Company Names", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 540, 920 ] }, { "parameters": { "resource": "chat", "model": "gpt-4", "prompt": { "messages": [ { "content": "=The company name is {{ $json.companyName }}. Identify if \"{{ $json.companyName }}\" could be a variation of one of the following names. If yes, return a JSON with match:true and company:the company name it matches. Else return a JSON with match:false.\n\nList of names:\n{{ $json.names }}" } ] }, "options": {} }, "id": "6dc57172-6250-4588-802c-74da7e664871", "name": "Check if there's a match", "type": "n8n-nodes-base.openAi", "typeVersion": 1, "position": [ 740, 920 ], "executeOnce": false, "credentials": { "openAiApi": { "id": "15k7VlACBlhT15Dv", "name": "OpenAi account" } } }, { "parameters": { "conditions": { "boolean": [ { "value1": "={{ $json.match }}", "value2": true } ] } }, "id": "f24d0989-1f2d-4f69-9f8b-5b6dff20ca0f", "name": "Does customer REALLY exist?", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [ 1140, 920 ] }, { "parameters": { "jsCode": "return JSON.parse($(\"Check if there's a match\").item.json.message.content);" }, "id": "1b87312a-e226-49c3-976c-b84187142175", "name": "Parse Match Response", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 940, 920 ] }, { "parameters": { "select": "channel", "channelId": { "__rl": true, "value": "G31SYNX50", "mode": "list", "cachedResultName": "senior-management" }, "text": "=A new client has just registered their details, but their legal company name is already registered in our system. For now, I have saved their data as {{ $('Typeform Trigger1').item.json['what is your legal company name?'] }}2.\n\nTODO: What would you like to do? ", "otherOptions": { "includeLinkToWorkflow": false } }, "id": "ce9a010e-7543-46a6-97e3-84c27eaa2bda", "name": "Notify Senior Management1", "type": "n8n-nodes-base.slack", "typeVersion": 2.1, "position": [ 1140, 500 ], "credentials": { "slackApi": { "id": "5bIQaYDelINKfH9L", "name": "Slack account - Zoe" } } }, { "parameters": { "jsCode": "let payload = $('Typeform Trigger1').all()[0].json;\n\npayload[\"what is your legal company name?\"] = payload[\"what is your legal company name?\"] + \"2\";\n\nreturn payload;" }, "id": "e6e89a9b-9b2c-416a-ac76-124a89749e79", "name": "Update Payload to add 2", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1640, 609 ] }, { "parameters": { "content": "## Does Client Already Exist?", "height": 842.1723782888396, "width": 876.9563416875615 }, "id": "8b1d54fd-407e-4de4-8317-6d9089fc8fc4", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 460, 460 ] }, { "parameters": { "select": "channel", "channelId": { "__rl": true, "value": "G31SYNX50", "mode": "list", "cachedResultName": "senior-management" }, "text": "=A new client has just registered their details, but I think they may already exist in our system. \n\nThe client that registered is {{ $('Get List of Existing Company Names').item.json.companyName }}\n\nAnd I think this may be the same as {{ $json.company }}\n\nIs it the same company or different?\n\nTODO: Handle responses (Note that if you reply, this won't do anything yet)", "otherOptions": { "includeLinkToWorkflow": false } }, "id": "d291248d-c71d-4f99-a473-aa0a608b46fc", "name": "Ask - Same Company or Different?", "type": "n8n-nodes-base.slack", "typeVersion": 2.1, "position": [ 1440, 609 ], "credentials": { "slackApi": { "id": "5bIQaYDelINKfH9L", "name": "Slack account - Zoe" } } }, { "parameters": { "content": "## Customer already exists", "height": 262.2275610115839, "width": 424.8563930836525 }, "id": "fcd371dc-08a7-47fe-a670-4a2886646231", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 1380, 540 ] }, { "parameters": { "content": "## Customer doesn't exist", "height": 450.4520653740113, "width": 859.3566184095582 }, "id": "fbcd63ff-b1b9-4c2c-92d2-e0e7b343dd9e", "name": "Sticky Note5", "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 1380, 848.2849063411204 ] }, { "parameters": { "jsCode": "return $('Typeform Trigger1').all()[0].json;" }, "id": "3715aa6c-3b2f-460d-ab04-77d589cc826b", "name": "Create Payload Juncture", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1140, 1120 ] }, { "parameters": { "jsCode": "const email = $input.all()[0].json[\"what is their email address?\"];\nconst domain = email.split(\"@\")[1];\n$input.all()[0].json.domain = domain;\nreturn $input.all()[0].json;" }, "id": "f751aad5-1ec0-4d1a-852b-3008f7e87ded", "name": "Extract Contacts Domain", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 1460, 920 ] }, { "parameters": { "select": "channel", "channelId": { "__rl": true, "value": "G31SYNX50", "mode": "list", "cachedResultName": "senior-management" }, "text": "=Congratulations! {{ $json.Name }} has been successfully registered as a new client!", "otherOptions": { "includeLinkToWorkflow": false } }, "id": "d665444e-41c8-4470-8464-c7e9ba03cb95", "name": "Send Congratulations", "type": "n8n-nodes-base.slack", "typeVersion": 2.1, "position": [ 1660, 1120 ], "credentials": { "slackApi": { "id": "5bIQaYDelINKfH9L", "name": "Slack account - Zoe" } } }, { "parameters": { "resource": "chat", "prompt": { "messages": [ {} ] }, "options": {} }, "id": "1cab6bdd-5621-4a27-894b-9369c00c50d4", "name": "OpenAI", "type": "n8n-nodes-base.openAi", "typeVersion": 1, "position": [ 1880, 1120 ], "credentials": { "openAiApi": { "id": "15k7VlACBlhT15Dv", "name": "OpenAi account" } } } ], "connections": { "Xero": { "main": [ [ { "node": "Send Congratulations", "type": "main", "index": 0 } ] ] }, "Typeform Trigger": { "main": [ [ { "node": "Check if Customer Exists", "type": "main", "index": 0 } ] ] }, "Supabase": { "main": [ [ { "node": "Xero", "type": "main", "index": 0 } ] ] }, "Dissect Address": { "main": [ [ { "node": "Parse Response", "type": "main", "index": 0 } ] ] }, "Parse Response": { "main": [ [ { "node": "Supabase", "type": "main", "index": 0 } ] ] }, "Check if Customer Exists": { "main": [ [ { "node": "Does Customer Exist?", "type": "main", "index": 0 } ] ] }, "Does Customer Exist?": { "main": [ [ { "node": "Notify Senior Management1", "type": "main", "index": 0 } ], [ { "node": "Check if Customer Exists1", "type": "main", "index": 0 } ] ] }, "Check if Customer Exists1": { "main": [ [ { "node": "Get List of Existing Company Names", "type": "main", "index": 0 } ] ] }, "Get List of Existing Company Names": { "main": [ [ { "node": "Check if there's a match", "type": "main", "index": 0 } ] ] }, "Check if there's a match": { "main": [ [ { "node": "Parse Match Response", "type": "main", "index": 0 } ] ] }, "Does customer REALLY exist?": { "main": [ [ { "node": "Ask - Same Company or Different?", "type": "main", "index": 0 } ], [ { "node": "Create Payload Juncture", "type": "main", "index": 0 } ] ] }, "Parse Match Response": { "main": [ [ { "node": "Does customer REALLY exist?", "type": "main", "index": 0 } ] ] }, "Notify Senior Management1": { "main": [ [ { "node": "Update Payload to add 2", "type": "main", "index": 0 } ] ] }, "Update Payload to add 2": { "main": [ [ { "node": "Extract Contacts Domain", "type": "main", "index": 0 } ] ] }, "Ask - Same Company or Different?": { "main": [ [ { "node": "Update Payload to add 2", "type": "main", "index": 0 } ] ] }, "Create Payload Juncture": { "main": [ [ { "node": "Extract Contacts Domain", "type": "main", "index": 0 } ] ] }, "Extract Contacts Domain": { "main": [ [ { "node": "Dissect Address", "type": "main", "index": 0 } ] ] }, "Send Congratulations": { "main": [ [ { "node": "OpenAI", "type": "main", "index": 0 } ] ] } } }

To Reproduce Steps to reproduce the behavior: I'm not quite sure. See above.

Expected behavior Expect that when you click "Execute previous nodes" or "Execute node" a second time, the data gets returned in the same way it did the first time.

Environment (please complete the following information):

Joffcom commented 9 months ago

Hey @jpardoe86,

Thanks for the report, I have taken a quick look and I was able to quickly reproduce it with the workflow below.

image

It looks like when you manually run a node it will try and use the first input and not the input that may actually contain the data. I have created an internal dev ticket for this which we are tracking as N8N-7017.

jpardoe86 commented 9 months ago

Amazing, thanks! Any ideas on a workaround for the time being? I found that using first() works instead of item for handling some instances, but that doesn't work when there are multiple items. Note the issue also happens after the LOOP node too.

Joffcom commented 9 months ago

Hey @jpardoe86,

The only workaround I can think of at the moment is to use the excute worklfow button at the bottom and make use of pinned data for any nodes that make network calls that will then at least allow you to test the workflow. The other possible workarounds are not great and would involve more complex workflows or deleting the branches when testing.

jpardoe86 commented 9 months ago

Okay great I'll try pinning the data. Thanks again!

pbrink231 commented 5 months ago

Did a dive into this issue and posting results here because I think these are related issues. Hoping this can help pinpoint where the bug is.

It seems "Test step" uses a different input than the workflow and even changes with subsequent uses. If pinned data, "Test step" seems to always use the first input in the input drop down list regardless of how the workflow goes. If unpinned data, "Test step" will use the first input in the input drop down list for the first run regardless of the workflow. It will then seem to use no input and not run at all but say "Workflow executed successfully" on all subsequent runs. If you go to the input node that is second on the input drop down list and click "Test run" it seems to "reset" the node after it. When you go to that node and click "Test run" it will work as if you ran it the first time by giving you data. But then all subsequent runs will not run at all like before.

Input drop down list: image

With pinned data or unpinned data. It seems it is with "Test step" specifically. "Test workflow" gives the correct output on the node based on the flow of the workflow.

I also noticed it usually says "Workflow executed successfully" instead of "Node executed successfully" in these strange scenarios after clicking "Test step".

Workaround

The easiest thing is to delete all branches except for the one I am testing. This works normally but I need to remember to reconnect everything after I finish testing.

A better solution is to create a sub workflow and have each one send to the sub workflow.

Scenarios showing the issues

Pinned Scenario with low value

low but showing high.json

image

Both low and high side have pinned data

high

[{
"value": 0.8,
"round": "high"
}]

low

[{
"value": 0.3,
"round": "low"
}]

The node "Add low value" adds a low value to the "value" key which is 0.3. The switch checks for > 0.5 so goes to Fallback case sending to "low side" in this case. "low side" sends its pinned date to "after round". That is the flow that is taken in this setup.

So what I should see when I go into "after round" is:

[{
"value": 0.3,
"round": "low"
}]

But what I am seeing is coming from the "high side" which is incorrect and not being hit in this workflow.

image

In this case "Test step" is using the data that comes from "high side" even though the workflow does not touch that step.

If I go to the canvas and use the "Test workflow" button I can see everything shows correctly.

image

image

If I click on "Test step", it will then show me the wrong data again

image

Unpinned scenario low value

In this scenario, I have everything unpinned. The flow will always go to the "low side".

low but showing nothing.json

image

When I am in "after round" I should always get

[{
"value": 0.3,
"round": "low"
}]

But I get nothing at all. When this happens it always says "Workflow executed successfully" instead of "Node executed successfully". Also the check mark does not show up on the "after round" node as if it was never actually hit when looking at the canvas.

image

image

Now if I go out to the canvas and click on "Test workflow" then you will see the workflow shows check marks for everything and if I go into "after round" I will see the correct output I expected.

image

image

Unpinned scenario with high value

Same as low but setting a high value of 0.8. I am including this scenario because it acts differently than the low scenario

high showing once then nothing.json

image

I expect when clicking "Test step" to get a value of:

[{
"value": 0.8,
"round": "high",
"my_field_1": "value"
}]

This actually works the first time.

image

But then when I click "Test step" a second or more times I get this.

image

I can "reset" it in a way by going to "low side" node and clicking "Test step" which shows no output data. Then go back to "after round" and click "Test step" and I get a value for the first run and then no value for second or more times after that.

It does not "reset" if I go to high side and click "Test step" like with low side.

Background on my use

image

I have a webhook that receives data with an action key and based on the value of that the results are slightly different. I need to get the Ids differently based on that action and send it to a flow. I have t