Workflow loop not working as I would expect

Describe the problem/error/question

In my workflow I use a loop to loop over a set of items. For each item I need to merge some data and execute an insert into a Google sheet.

This works fine for the first item.
At the second item it only executes part of the workflow (the creation of the sheet) and nothing gets inserted.
After that, the loop just ends. Only 2 of 4 items get processed.

I think this could happen because I use multiple inputs to merge some data which causes the loop to break, but I’m not sure.

What is the error message (if any)?

There is no error

Please share your workflow

{
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "85cdde2b7928d4076a1207d87419cceb8803794e47cbaa068efe250b79bcd360"
  },
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "months",
              "triggerAtHour": 8
            }
          ]
        }
      },
      "id": "c54c4835-006e-4218-8b1b-a5c50f6b2e43",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        1260,
        520
      ]
    },
    {
      "parameters": {
        "fields": {
          "values": [
            {
              "name": "company",
              "type": "arrayValue",
              "arrayValue": "=[\n    {\n        \"name\": \"Company 1\",\n        \"company_id\": \"id1\",\n        \"spreadsheet_id\": \"id1\"\n    },\n    {\n        \"name\": \"Company 2\",\n        \"company_id\": \"id2",\n        \"spreadsheet_id\": \"id2\"\n    },\n    {\n        \"name\": \"Company 3\",\n        \"company_id\": \"id3\",\n        \"spreadsheet_id\": \"id3\"\n    },\n    {\n        \"name\": \"Company 4\",\n        \"company_id\": \"id4\",\n        \"spreadsheet_id\": \"id4\"\n    }\n]"
            }
          ]
        },
        "include": "none",
        "options": {}
      },
      "id": "d6709a03-101c-4f8c-9b38-8bd6f64f2dae",
      "name": "Set variables",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.2,
      "position": [
        1600,
        520
      ]
    },
    {
      "parameters": {
        "fieldToSplitOut": "company",
        "options": {}
      },
      "id": "379a9178-423d-4d8a-9ccd-70be29935b17",
      "name": "Turn object into items",
      "type": "n8n-nodes-base.splitOut",
      "typeVersion": 1,
      "position": [
        1900,
        520
      ]
    },
    {
      "parameters": {
        "url": "https://api.awork.com/api/v1/timeentries",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "orderby",
              "value": "createdOn desc"
            },
            {
              "name": "filterby",
              "value": "=project/company/id eq guid'{{ $json.company_id }}' and task/taskStatus/type eq 'done' and not isBilled"
            }
          ]
        },
        "options": {}
      },
      "id": "da693916-e108-42fe-8c10-9ee687fdc004",
      "name": "Get timeentries",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        3000,
        600
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "GcslaAdjK3yEcAoH",
          "name": "awork"
        }
      }
    },
    {
      "parameters": {
        "workflowId": "0leN0ZrO4zOFNLU7",
        "options": {}
      },
      "id": "5bb6f31e-52b7-4efb-b269-2186de3c266a",
      "name": "Fetch users",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1,
      "position": [
        2800,
        1400
      ]
    },
    {
      "parameters": {
        "mode": "combine",
        "mergeByFields": {
          "values": [
            {
              "field1": "userId",
              "field2": "user_id_awork"
            }
          ]
        },
        "options": {}
      },
      "id": "96b0f972-6515-4d12-8a61-996e4b6ef67b",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 2.1,
      "position": [
        3260,
        600
      ]
    },
    {
      "parameters": {
        "jsCode": "const groupedTimeEntries = {};\n\nfor (const timeEntry of $input.all()) {\n  if (!timeEntry.json.task) continue;\n\n  const { task, taskId, duration } = timeEntry.json;\n  const { name, closedOn } = task;\n\n  const parentTaskName = task.parent ? task.parent.name : '';\n\n  // convert duration in seconds to hours and format to two decimal places\n  const durationInHours = (duration / 3600).toFixed(2).toString().replace('.', ',');\n\n  // set a standard structure or add to the duration properties\n  if (!groupedTimeEntries[taskId]) {\n    groupedTimeEntries[taskId] = {\n      taskName: name,\n      parentTask: parentTaskName,\n      projectName: task.project.name,\n      duration: duration,\n      durationInHours: durationInHours,\n      closedOn: closedOn,\n      aworkTaskLink: `=HYPERLINK(\"https://navy-famous-heroes.awork.io/tasks/${taskId}/details\"; \"Awork Task\")`,\n      setTimesAsAccountedLink: `=HYPERLINK(\"https://n8n.wacg.dev/webhook/awork-set-billed/?task_id=${taskId}&token=hLcLDkAzB2CHerChTq\"; \"Zeiten auf abgerechnet stellen\")`\n    };\n  } else {\n    groupedTimeEntries[taskId].duration += duration;\n    groupedTimeEntries[taskId].durationInHours = ((groupedTimeEntries[taskId].duration / 3600).toFixed(2)).toString().replace('.', ',');\n  }\n}\n\nreturn {\n  json: {\n    items: [\n      [\n        'Aufgabenname',\n        'Hauptaufgabe',\n        'Projekt',\n        'Dauer in Sek.',\n        'Dauer in Std.',\n        'Geschlossen am',\n        'Awork Link',\n        'Abrechnen'\n      ],\n      ...Object.values(groupedTimeEntries).map(Object.values)\n    ],\n  }\n}"
      },
      "id": "3f33c77e-f4bb-4022-9f6f-9cff121cb0bd",
      "name": "Code",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3480,
        600
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://sheets.googleapis.com/v4/spreadsheets/{{ $json.spreadsheetId }}/values/{{ $json.replies[0].addSheet.properties.title }}:append?valueInputOption=USER_ENTERED",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "values",
              "value": "={{ $json.items }}"
            }
          ]
        },
        "options": {}
      },
      "id": "cbfe98a1-6e03-4931-b324-ee90e1aebcb5",
      "name": "Append cell data",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        3860,
        320
      ],
    },
    {
      "parameters": {
        "mode": "combine",
        "combinationMode": "mergeByPosition",
        "options": {}
      },
      "id": "2f1e7f49-6991-464d-958c-39bbc3bf175c",
      "name": "Merge1",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 2.1,
      "position": [
        3680,
        320
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://sheets.googleapis.com/v4/spreadsheets/{{ $json.spreadsheet_id }}:batchUpdate",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"requests\": [\n    {\n      \"addSheet\": {\n        \"properties\": {\n          \"title\": \"{{ $now.minus({ months: 1 }).toFormat('yyyy-MM') }}-N8N\"\n        }\n      }\n    }\n  ]\n}\n",
        "options": {}
      },
      "id": "62049dd7-f10f-4501-b44a-09f1ad5afa05",
      "name": "Create worksheet",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        3020,
        320
      ],
      "credentials": {
        "googleApi": {
          "id": "myXTnQoXNR5c917j",
          "name": "Google Service Account account"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://sheets.googleapis.com/v4/spreadsheets/{{ $json.spreadsheetId }}:batchUpdate",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "googleApi",
        "sendBody": true,
        "specifyBody": "=json",
        "bodyParameters": {
          "parameters": [
            {}
          ]
        },
        "jsonBody": "={\n  \"requests\": [\n    {\n      \"repeatCell\": {\n        \"range\": {\n          \"sheetId\": \"{{ $('Merge1').item.json.replies[0].addSheet.properties.sheetId }}\",\n          \"startRowIndex\": 0,\n          \"endRowIndex\": 1\n        },\n        \"cell\": {\n          \"userEnteredFormat\": {\n            \"textFormat\": {\n              \"bold\": true\n            }\n          }\n        },\n        \"fields\": \"userEnteredFormat.textFormat.bold\"\n      }\n    },\n    {\n      \"updateSheetProperties\": {\n        \"properties\": {\n          \"sheetId\": \"{{ $('Merge1').item.json.replies[0].addSheet.properties.sheetId }}\",\n          \"gridProperties\": {\n            \"frozenRowCount\": 1\n          }\n        },\n        \"fields\": \"gridProperties.frozenRowCount\"\n      }\n    },\n    {\n      \"autoResizeDimensions\": {\n        \"dimensions\": {\n          \"sheetId\": \"{{ $('Merge1').item.json.replies[0].addSheet.properties.sheetId }}\",\n          \"dimension\": \"COLUMNS\",\n          \"startIndex\": 0,\n          \"endIndex\": 8\n        }\n      }\n    },\n    {\n      \"sortRange\": {\n        \"range\": {\n          \"sheetId\": \"{{ $('Merge1').item.json.replies[0].addSheet.properties.sheetId }}\",\n          \"startRowIndex\": 1,\n          \"endRowIndex\": null,\n          \"startColumnIndex\": 0,\n          \"endColumnIndex\": null\n        },\n        \"sortSpecs\": [\n          {\n            \"dimensionIndex\": 2,\n            \"sortOrder\": \"ASCENDING\"\n          },\n          {\n            \"dimensionIndex\": 1,\n            \"sortOrder\": \"ASCENDING\"\n          }\n        ]\n      }\n    },\n    {\n      \"addBanding\": {\n        \"bandedRange\": {\n          \"range\": {\n            \"sheetId\": \"{{ $('Merge1').item.json.replies[0].addSheet.properties.sheetId }}\",\n            \"startRowIndex\": 0,\n            \"endRowIndex\": null\n          },\n          \"rowProperties\": {\n            \"firstBandColor\": {\"red\": 1.0, \"green\": 1.0, \"blue\": 1.0},\n            \"secondBandColor\": {\"red\": 0.9, \"green\": 0.9, \"blue\": 0.9}\n          }\n        }\n      }\n    }\n  ]\n}",
        "options": {}
      },
      "id": "42c8ca27-29bb-4567-8677-bd8bd639d638",
      "name": "Format sheets",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        4040,
        320
      ],
      "credentials": {
        "googleApi": {
          "id": "myXTnQoXNR5c917j",
          "name": "Google Service Account account"
        }
      }
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "c1d8e5a1-3958-4e8b-8ce8-a5dbec677947",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        2520,
        720
      ]
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Set variables",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch users",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set variables": {
      "main": [
        [
          {
            "node": "Turn object into items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Turn object into items": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get timeentries": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch users": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Append cell data": {
      "main": [
        [
          {
            "node": "Format sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge1": {
      "main": [
        [
          {
            "node": "Append cell data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create worksheet": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format sheets": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        null,
        [
          {
            "node": "Get timeentries",
            "type": "main",
            "index": 0
          },
          {
            "node": "Create worksheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {}
}

Information on your n8n setup

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

@timkley , the Loop gets broken when a node in the loop does not return an item. You are using 2 Merge nodes in the loop. I suspect at one those the match is not found that breaks completing the loop.

I suspect you might even get by without a loop. At least it gives you more chances for the workflow to complete as well as easier to see where the problem is when encountered.

Thank you very much for your answer!

Your example is exactly what I had at first, but thats also not the result I was expecting.

The lower node (starting at Get timeentries) only executes 1 instead of four times. Here all time entries are returned instead of only the subset for one company which results in only 1 item returned by the Code node, instead of the expected four.

Now I could adjust the Code node for that and again group by the company, but it felt redundant and really what I wanted was to just execute everything after “Turn object into items” N times, once for every company.

The original workflow seems to stop at “Merge” where the time entries are matched to the users, this one is only executed once. Might it be, that because the “Fetch users” node is outside of the loop the merge does not get executed again? I didn’t include it in the node because I wanted to reuse the data returned since it hits an external API.

Thanks again :slight_smile:

@timkley ,

The lower node (starting at Get timeentries) only executes 1 instead of four times.

If that is the case, then nothing after “Get timeentries” would caused that as this node is executed before anything would play part into its execution. It has to produce 4 items (I expet to see number 4 accompanying the exit of the node).

What do you get if you execute only part of the flow as below?

Perhaps your problem lies in not handling the output of “Get timeentries” correctly?

According to the API documentation the node returns the data in the format

[
  {
    . . .
    "taskId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "projectId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    . . .
    "user": {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "firstName": "string",
      "lastName": "string",
      "hasImage": true
    },
    . . .
  }
]

Note that to get to the user ID the path is user.id. However, in the Merge node you match by userId instead. Hence you cannot go beyond the Merge node as no match could be found.

There are 4 API calls taking place but it doesn’t return 4 groups of N items but all items grouped together. I’m suspecting my expectations are wrong that this would cause four separate “groups” of executions?

I’ve verified that userId is part of the response, the merge is working as expected.

I think it is a good news for your use case. That would mean you do not need to use Loop node. I assume your “Fetch users” flow is applicable to any of the 4 groups and running it just once should sufice.

You do need, however, group all those time entries by the company to be able to populate separate spreadsheets. Basically what you tried before the loop. I don’t expect it to be a difficult task especially since n8n introduced Summarize node.

1 Like

That’s what I did in the end using the code node because I saw too late, that the summarize can not only summarize but split also.

Thank you very much for your assistance and have a great day!

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