Nested loop

Describe the problem/error/question

Because my business logic requires nested loops, such as iterating through each channel and then iterating through each program within that channel. On other platforms, I achieved this using loops and variables, such as coze and dify. n8n offers a better plugin ecosystem, so I wanted to migrate here, but encountered two issues.

  1. There seem to be no loop variables or workflow variables here, so I can’t simply use variables to store intermediate results.

  2. The strangest part is nested loops. For example, the outer loop runs 5 times, and the inner loop runs 4 times, so it should execute the code node 20 times. However, in reality,

4 – it is ok
1 – inner loop logic not executed, just in and out
1
1
1

the last 4 rounds of the inner loop loop IN are not executed, and it returns directly, which is very strange.

What is the error message (if any)?

no

Please share your workflow


(Select the nodes on your canvas and use the keyboard shortcuts CMD+C/CTRL+C and CMD+V/CTRL+V to copy and paste the workflow.)

{
“nodes”: [
{
“parameters”: {
“fieldToSplitOut”: “=value”,
“options”: {}
},
“type”: “n8n-nodes-base.splitOut”,
“typeVersion”: 1,
“position”: [
680,
-445
],
“id”: “91de3cb9-8b36-42e4-8c2e-02e75c7d41e7”,
“name”: “Split Out1”,
“alwaysOutputData”: true
},
{
“parameters”: {},
“type”: “n8n-nodes-base.manualTrigger”,
“typeVersion”: 1,
“position”: [
-640,
-340
],
“id”: “4841b7a4-8fd4-4686-b2e5-360139f3809d”,
“name”: “When clicking ‘Execute workflow’”
},
{
“parameters”: {
“assignments”: {
“assignments”: [
{
“id”: “c77ca932-3519-4041-bd24-2a602b6f1f54”,
“name”: “value”,
“value”: “[{"test":"1"},{"test":"2"},{"test":"3"},{"test":"4"}]”,
“type”: “array”
}
]
},
“options”: {}
},
“type”: “n8n-nodes-base.set”,
“typeVersion”: 3.4,
“position”: [
520,
-440
],
“id”: “14657e8d-380c-4303-b629-07240127e2e9”,
“name”: “Edit Fields”
},
{
“parameters”: {
“assignments”: {
“assignments”: [
{
“id”: “53cc3e4d-396f-49aa-9d78-44c984122cac”,
“name”: “num”,
“value”: “={{ Array.from({ length: 5 }, (_, i) => i) }}”,
“type”: “array”
}
]
},
“options”: {}
},
“type”: “n8n-nodes-base.set”,
“typeVersion”: 3.4,
“position”: [
-420,
-340
],
“id”: “28235994-496b-45a7-94c3-0232ee4a522c”,
“name”: “Edit Fields1”
},
{
“parameters”: {
“fieldToSplitOut”: “num”,
“options”: {}
},
“type”: “n8n-nodes-base.splitOut”,
“typeVersion”: 1,
“position”: [
-200,
-345
],
“id”: “099c8d20-a448-4e95-a925-3d9af9ae96d0”,
“name”: “Split Out2”
},
{
“parameters”: {
“jsCode”: “\n\n// 1. 在第一次迭代时初始化一个数组\nif (this.constructor.finalList === undefined) {\n this.constructor.finalList = ;\n}\n\n// 3. 将当前结果 append 到我们存储的数组中\nthis.constructor.finalList.push($(‘Loop Out’).first().json.num);\n\nreturn $input.all();”
},
“type”: “n8n-nodes-base.code”,
“typeVersion”: 2,
“position”: [
1480,
-360
],
“id”: “b5f2d0b3-87da-4ca3-8a37-66c0ae254bb6”,
“name”: “Code”
},
{
“parameters”: {
“jsCode”: “// [在循环外部的 Code 节点]\n\n// 直接访问在循环中被填充的那个静态变量\n// 因为循环已经结束,所以 finalList 此刻是完整的\nconst finalResultList = this.constructor.finalList;\n\n// 将最终的列表作为这个节点的输出\n// 最好将其包装在一个 JSON 对象中,方便后续处理\nreturn [{\n json: {\n myAppendedList: finalResultList,\n }\n}];\n”
},
“type”: “n8n-nodes-base.code”,
“typeVersion”: 2,
“position”: [
280,
-640
],
“id”: “1b765c34-c54a-44db-8336-7514a829a63e”,
“name”: “Code1”
},
{
“parameters”: {
“options”: {}
},
“type”: “n8n-nodes-base.splitInBatches”,
“typeVersion”: 3,
“position”: [
960,
-380
],
“id”: “9eefcd70-4058-42ab-9fe3-a09fe6696c31”,
“name”: “Loop IN”,
“executeOnce”: false
},
{
“parameters”: {
“options”: {}
},
“type”: “n8n-nodes-base.splitInBatches”,
“typeVersion”: 3,
“position”: [
20,
-340
],
“id”: “313201fe-6326-4789-bc84-9219249cfa6e”,
“name”: “Loop Out”,
“executeOnce”: false
}
],
“connections”: {
“Split Out1”: {
“main”: [
[
{
“node”: “Loop IN”,
“type”: “main”,
“index”: 0
}
]
]
},
“When clicking ‘Execute workflow’”: {
“main”: [
[
{
“node”: “Edit Fields1”,
“type”: “main”,
“index”: 0
}
]
]
},
“Edit Fields”: {
“main”: [
[
{
“node”: “Split Out1”,
“type”: “main”,
“index”: 0
}
]
]
},
“Edit Fields1”: {
“main”: [
[
{
“node”: “Split Out2”,
“type”: “main”,
“index”: 0
}
]
]
},
“Split Out2”: {
“main”: [
[
{
“node”: “Loop Out”,
“type”: “main”,
“index”: 0
}
]
]
},
“Code”: {
“main”: [
[
{
“node”: “Loop IN”,
“type”: “main”,
“index”: 0
}
]
]
},
“Code1”: {
“main”: [

]
},
“Loop IN”: {
“main”: [
[
{
“node”: “Loop Out”,
“type”: “main”,
“index”: 0
}
],
[
{
“node”: “Code”,
“type”: “main”,
“index”: 0
}
]
]
},
“Loop Out”: {
“main”: [
[
{
“node”: “Code1”,
“type”: “main”,
“index”: 0
}
],
[
{
“node”: “Edit Fields”,
“type”: “main”,
“index”: 0
}
]
]
}
},
“pinData”: {},
“meta”: {
“templateCredsSetupCompleted”: true,
“instanceId”: “bca2e2865f2a489a09ebfc27bc2cef70bbb94badc96bd6aaf3a5276e0c6aef21”
}
}

Share the output returned by the last node

Information on your n8n setup

  • n8n version: cloud version
  • Database (default: SQLite):
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app):
  • Operating system:

I think that’s because the Loop In can only be executed once. So other dataset didn’t pass to Loop in. Just go to done instead.

Can you think about your scenario and sub-workflow? Maybe that will help you.

Because I can’t tell what you want to acheive in your code node. So not easy to help to build a test demo with sub-workflow.

Hello

Thank you for prompt solution.

My request seems to be a very common scenario: a nested loop. For example, I want to iterate through a YouTube channel and extract subtitles from some of the videos within that channel. I plan to use a sub-workflow to implement the inner loop.

Another issue is that I want to store the subtitles for each video in a JSON array. Typically, this can be resolved using a global variable. However, I haven’t found a variable concept in n8n, so I’m unsure how to implement this. Specifically, how can I pass data between the main workflow and the sub workflow (I don’t want to use something like Google Sheets for persistent storage, as I’ll need to summarize the data later)?

You take take a look at this getWorkflowStaticData

This might help you achieve what you want.

You can save data directly in the workflow. This data should be small.

As it didn’t say how small it should be. Maybe worth a try then.


Pass data to sub-workflow is easy. But you might need some node to collect the information you need.

Please take a look at that:

If you run it, you can see more in detail what’s really happening - how n8n treats the loops. So, the thing is, that each “Loop over items” holds internal context: $node["Loop Over Items"].context - where it sets items it already processed. It looks like this:

{
      "currentRunIndex": 9,
      "maxRunIndex": 10,
      "sourceData": {
        "previousNode": "DebugHelper1"
      },
      "items": [],
      "processedItems": [
        {
          "json": {
            "nanoId": "zpgCFEURlX9PHtN3"
          },
          "pairedItem": {
            "sourceOverwrite": {
              "previousNode": "Wait"
            },
            "item": 0
          }
        },
...
      ],
      "noItemsLeft": true,
      "done": false
    }

So, after inner loop executes once, it’s state is already at noItemsLeft = true, hence every next outer loop input is treated as executed.

How to solve it? Generally n8n team proposes putting inner loop into a sub-workflow. If it’s not your thing, you can hack it a little bit. There’s a “Reset” option in loop node which will reset the internal context, however you should define proper expression on when it should reset. By default it will reset on every loop item iteration, falling into infinite loop. It can be changed tho. Here’s updated flow, which resets the loop properly on inner loop:

So, in your case, you should set Reset option and set it to expression: {{ $prevNode.name === 'Split Out1' }} - then it will reset its context each time that it’s called by outer loop.

Thank you very much, i have implemented sub-workflow, it works well.

By the way, how to collect some temperate result during looping? there is no variable (list e.g.) where i can cache data.

E.g. I want loop through a list of items. If a find the right one, i would like to break and continue to next node.

thanks

Check out getWorkflowStaticData | n8n Docs