How do you wait for all nodes to finish execution before continuing?

Disclaimer: I’m new to n8n.

I’m trying to do some tinkering and found this useful flow to archive my weekly “Discover” playlist on spotify. It’s particularly useful because each week they overwrite the playlist with new songs, so I’d like to keep an archive of them.

Workflow: https://n8n.io/workflows/697

{
  "nodes": [
    {
      "name": "Get Playlists",
      "type": "n8n-nodes-base.spotify",
      "position": [
        300,
        680
      ],
      "parameters": {
        "resource": "playlist",
        "operation": "getUserPlaylists",
        "returnAll": true
      },
      "credentials": {
        "spotifyOAuth2Api": "TBD"
      },
      "executeOnce": false,
      "typeVersion": 1
    },
    {
      "name": "Get Tracks",
      "type": "n8n-nodes-base.spotify",
      "position": [
        850,
        680
      ],
      "parameters": {
        "id": "={{$node[\"Find Weekly Playlist\"].json[\"uri\"]}}",
        "resource": "playlist",
        "operation": "getTracks",
        "returnAll": true
      },
      "credentials": {
        "spotifyOAuth2Api": "TBD"
      },
      "executeOnce": true,
      "typeVersion": 1,
      "alwaysOutputData": false
    },
    {
      "name": "Save to Archive",
      "type": "n8n-nodes-base.spotify",
      "position": [
        1000,
        680
      ],
      "parameters": {
        "id": "={{$item(0).$node[\"Find Archive Playlist\"].json[\"uri\"]}}",
        "trackID": "={{$json[\"track\"][\"uri\"]}}",
        "resource": "playlist"
      },
      "credentials": {
        "spotifyOAuth2Api": "TBD"
      },
      "executeOnce": false,
      "typeVersion": 1
    },
    {
      "name": "Find Archive Playlist",
      "type": "n8n-nodes-base.if",
      "position": [
        500,
        600
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json[\"name\"]}}",
              "value2": "Discover Weekly Archive"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "name": "Cron",
      "type": "n8n-nodes-base.cron",
      "position": [
        150,
        680
      ],
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "mode": "custom",
              "cronExpression": "0 0 8 * * 1"
            }
          ]
        }
      },
      "notesInFlow": false,
      "typeVersion": 1
    },
    {
      "name": "Find Weekly Playlist",
      "type": "n8n-nodes-base.if",
      "position": [
        500,
        750
      ],
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json[\"name\"]}}",
              "value2": "Discover Weekly"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        700,
        680
      ],
      "parameters": {
        "mode": "wait"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Cron": {
      "main": [
        [
          {
            "node": "Get Playlists",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Get Tracks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Tracks": {
      "main": [
        [
          {
            "node": "Save to Archive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Playlists": {
      "main": [
        [
          {
            "node": "Find Archive Playlist",
            "type": "main",
            "index": 0
          },
          {
            "node": "Find Weekly Playlist",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Weekly Playlist": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Find Archive Playlist": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

However, I also would like a notification when it’s completed. I have Telegram setup correctly which can figure me a message. But if I add it to the end of this workflow, I get basically 30 notifications because it’s executing for each track that’s added to the archive.

I assume this is a common issue with an easy workaround? How does one do workflows that involve nodes with multiple events, but wait for all events to finish before doing something else? (In my case, fire a single Telegram msg when all complete).

Do you want a simple message that it is done or do you want to add the names of all the songs or the number of songs added?

If you really just want to send a single message instead of multiple ones you can open the Telegram-Node, select “Settings” (next to “Parameters”), and there activate “Execute Once”. Then the node will only execute one time (for the first item) instead of once for each.

Thanks for the tip. I want the message once. That solution seems rather basic though as there is no sense of error handling that I can tell. How would I send one message on complete success vs error if there’s at least one failure?

As soon as one fails the whole workflow fails. If you have an Error Workflow set it would then be called.

Hey @tmchow!

As @jan mentioned, you can have a Error Workflow, which will execute whenever there is an error. You can learn more about how to create an Error Workflow with the Error Trigger node on the official documentation.

@harshil1712 @jan If I’m not mistaken, the error node is only triggered on error, but how would I do logic like:

  1. If there’s any error, fire Telegram Message X
  2. If no errors, fire Telegram Message Y

If seems like with Error node, i can only detect an error and do something, but can’t know if everything was succesful, correct?

In your regular workflow, you would only have the Telegram node to send Message Y.

In the error workflow, you would have another Telegram node that would send message X.

Sorry, I’m not following.

If there are 10 executions of a node, and 4 succeed, but the 5th fails:

  1. the telegram node will send Message Y at the first success but not anymore since I have setting enabled to “execute once”.
  2. The Telegram Node will send Message X for error at the 5th
  3. Workflow then stops because of error

What I think is desired in my case, and most others, would be to not fire Message Y at the first success, but rather key off the fact there were no errors.

That would only work if you send one message.

If you would process multiple messages at the same time you would have to set on the node (the one you expect to error) the option “Continue on Error”. It would then simply output an empty object if it fails.

You would then have to use a Merge-Node to add back the data from before (that you know which request did fail). The successful items will then have properties that the none successful ones do not have. Depending on that you can then send one message or the other with the help of an IF-Node.

Oy, that’s a lot of steps for what I’d think it fairly common for medium complexity automations. Maybe there’s an opportunity for a “Success” trigger (similar to “Error” trigger)? The way that could behave is if the node entirely completes with no errors, this node is triggered.

Actually, until now the need for something like that did not come up too much (or at least not many people asked for it because they found a good enough workaround).
And do not understand what you mean by many nodes. We are talking about 2 (Merge and IF) which is exactly the same amount as a Success-Trigger and an Error-Trigger (but to be honest do not really understand the proposed “opportunity”).

If I’m understand the proposal correctly, the you need to use MERGE and IF nodes connected within the workflow, but a ERROR and SUCCESS node live in the workflow but disconnected, so is more flexible. Am I wrong?

Still do not think I understand. What do you mean with “ERROR and SUCCESS node live in the workflow but disconnected”. Still sounds like 2 nodes to me. Also wonder about the disconnected part.

I’m talking about this: Notice the MERGE is connected within the workflow and acts only on that particular segment. But the ERROR node is disconnected and presumably covering any error that occurs anywhere in the workflow. Or am I understanding this incorrectly?

Error Workflows are totally separate workflows. It is not recommended to set a workflow as its own Error-Workflow.
Here a blog post which explains how they work:

Sorry I’m being dense, but this might be my confusion over terminology. But if I’m reading this correctly, if the error trigger is triggered whenever any active workflow has an error?

If this correct?

So if i have 3 workflows active, and each one has an error, all error triggers will be invoked?

Yes if the workflows that error have an Error-Workflow set and they error, they will trigger the Error-Workflow. So if three different workflows have the same Error-Workflow set, they would trigger it 3x. But each workflow would trigger it max. 1x per execution. So no matter if the workflow execution has 1 item or 10.000 it would trigger the Error-Workflow only 1x.