Sometimes, in Google Tag Manager’s Debug mode, you’ll see tags appear with the status Still Running, and you’ll (eventually) notice that these tags are not doing what they are supposed to be doing.

When you see this message on a tag, it technically means this:

The tag failed to signal Google Tag Manager that it is “done”.

The technical explanation is, naturally, too simple to be useful. In this article, I’ll explore what “done” means, and how especially Google Analytics tags manifest this behavior.

If you’re only interested in the latter, feel free to jump to the relevant section of this article.

X

The Simmer Newsletter

Subscribe to the Simmer newsletter to get the latest news and content from Simo Ahava into your email inbox!

When is a tag “done”?

By default, a tag is done when its code is executed, and the browser is ready to move on to the next script block in queue. This can happen if the browser reaches the end of the script in turn to be executed, or if the script throws an error that is not caught.

For example, take a Custom HTML Tag that looks like this:

<script>
  console.log('Hello!');
</script>

When it’s time to fire this tag, Google Tag Manager injects this tag to the end of <body> in the document object model, and then the browser proceeds to execute the code within the <script> block.

As this is all run synchronously, by the time the browser has written Hello! to the console, it’s ready to move to the next script waiting to be executed, and GTM will thus know that this tag is now “done”. Preview mode will show that the tag has Succeeded.

What about if we have an asynchronous operation in the middle of the tag?

Let’s simulate async with a simple timeout.

<script>
  window.setTimeout(function() {
    console.log('Hello!');
  }, 5000);
</script>

This time, when the tag’s JavaScript is executed, the browser waits five seconds before writing to the console. So when does GTM signal tag completion? No, not after 5 seconds, but rather after the browser reaches the end of the setTimeout expression.

With asynchronous operations, Google Tag Manager doesn’t wait. As with synchronous scripting, as soon as the browser reaches the end of the code block, the tag has Succeeded, regardless of how many asynchronous operations are still waiting for completion.

You can instruct Google Tag Manager to wait for an asynchronous operation to complete by utilizing callbacks. There are four different callbacks you’d use, depending on use case, to signal Google Tag Manager that a tag has run its course:

  1. window.google_tag_manager[{{Container ID}}].onHtmlSuccess({{HTML ID}}) - this is the callback you’d use in Tag Sequencing to signal the next tag in the sequence that the current tag has completed successfully. It requires the Container ID and HTML ID Built-in Variables to be activated.

  2. window.google_tag_manager[{{Container ID}}].onHtmlFailure({{HTML ID}}) - this is the callback you’d use in Tag Sequencing to signal the next tag in the sequence that the current tag failed in its execution.

  3. data.gtmOnSuccess() - this is the callback you’d use in custom tag templates to signal the tag has completed successfully.

  4. data.gtmOnFailure() - this is the callback you’d use in custom tag templates to signal the tag has failed in its execution.

So, if we wanted the browser to wait for the setTimeout call to complete, we’d use it like this:

<script>
  window.setTimeout(function() {
    console.log('Hello!');
    window.google_tag_manager[{{Container ID}}].onHtmlSuccess({{HTML ID}});
  }, 5000);
</script>

If invoking the callback with a significant delay (e.g. 5 seconds), you might see the tag as Still Running in Preview mode only for as long as the timeout lasts. As soon as the success callback is invoked, the status will change from Still Running to Succeeded (or Failure if that’s the callback you ran with).

So why do tags stay in “Still running” state?

If you recall from the previous chapter, if the tag has no callbacks, GTM will signal completion once it reaches the end of the code block or if the code block throws a syntax error. Thus there’s no actual way for a tag like this to signal it is “Still running”, as if the browser never reaches the end of the code block and the code never throws an error, it means the code is stuck in an infinite loop or memory leak, and the whole browser would freeze as a result.

However, as soon as you add either the google_tag_manager[...] callbacks in a Custom HTML tag or data callbacks in a custom tag template, GTM will actively wait for these callbacks to execute before signalling the tag is done.

Ergo, if the browser never reaches these callbacks when they are nevertheless present in the code, GTM will never receive the completion signal and will remain in “Still running” state.

Here’s an example that will result in the tag being stuck in “Still running” limbo.

<script>
  if (false) {
    window.google_tag_manager[{{Container ID}}].onHtmlSuccess({{HTML ID}});
  }
</script>

The browser will never reach the expression within the if {...} block because false will never evaluate to true. Thus, GTM has detected a success callback written in the code block, and it anxiously waits for it to be executed, but it never will. Thus in GTM’s view, the tag is perpetually running.

You can replace the callback with onHtmlFailure() to achieve the same result. It’s enough for either callback to be present for Google Tag Manager to actively wait for resolution.

With tag templates, it’s the same thing. If you run this:

if (false) {
  data.gtmOnSuccess();
}

This template would always have its tags be stuck in “Still running” state, since there’s no way to reach that callback.

The lesson to be learned here?

Whenever you use callbacks in Custom HTML tags, or whenever you create custom tag templates, make sure at least SOME callback is always reached.

Google Analytics tags “Still running”

Many of us have experienced the “Still running” phenomenon with Google Analytics tags.

Based on what we learned in the previous chapters, we’re now at an impasse. Google Analytics tags are not Custom HTML tags that you can fix the callbacks in, nor are they custom tag templates that you can modify to have the callback be invoked. So what are they?

They are tag templates, where the success callback is called as soon as the Google Analytics tag’s hitCallback method is reached. This hitCallback is automatically added by Google Tag Manager (preserving any hitCallback you add manually to the tag).

So, when a Google Analytics tag is “Still running”, it means its hitCallback was never reached.

How does this happen? Here are the three most common reasons.

1. Using an incorrect variable in the Tracking ID field

The Tracking ID field in your Google Analytics tags (in Google Tag Manager) is reserved for the tracking ID. The tracking ID is your property’s UA-XXXXXX-Y string.

Thus, the only thing you can have in a Tracking ID field is that string, or a variable which returns that string and nothing else.

It’s still extremely common to see people add a Google Analytics Settings variable to the Tracking ID field. This results in the Tracking ID field value resolving to an object (of your Google Analytics settings) rather than the required UA-XXXXX-Y string.

The result is that a tracker is created with incorrect settings, and the hitCallback is never reached.

Best way to debug this is to use the Google Analytics Debugger browser extension, and checking the console output. If you see something like this, your setup is broken:

How to fix this?

Simple. Never use a Google Analytics Settings variable anywhere else except a Google Analytics Settings field. Always check and double-check that the Tracking ID field contains a value or variable that resolves to UA-XXXXXX-Y.

2. The ga() method is hijacked by some other code running on the site

If some other tool overwrites the global ga() method, it means that there is no longer such a thing as a “Google Analytics hit”, or “Google Analytics tracker”, and as a consequence the hitCallback is never reached. Google Tag Manager sees the callback in the code and thus waits for it, but because the global method no longer works, the callback is never reached.

Easiest way to verify this is to open the JavaScript console of your page, and running the following command:

console.log(window[window['GoogleAnalyticsObject']].answer);

If the method has not been overwritten, you should see the number 42 written into the console. This is a sign that the analytics.js library still works with the global method.

If you see anything else, most likely either undefined or an error, it would mean that there’s something wrong with the global method.

NOTE! Ad blockers should not have a say here. If the Google Analytics endpoint is blocked, the hitCallback would still be reached, and if the entire analytics.js library is blocked, the ga() command would never be invoked in the first place.

How to fix this?

You can change the global method name to e.g. my_ga in your Google Analytics tags (in GTM) by expanding Advanced Configuration and updating the Global Function Name field. This must be done consistently in all your tags, so you might want to use a Google Analytics Settings variable.

If you’re using analytics.js, you can rename the method name in the snippet.

3. Nonexistent Optimize container loaded on the site

This is super annoying. It’s a bona fide bug in my opinion, and I’ve reported it as such.

If you create a Google Optimize tag, it actually loads the Optimize container as a Universal Analytics plugin. The reason for this is that the plugin is used to dispatch experiment data to Google Analytics.

The problem is that in case the Optimize container does not exist (you’ve deleted it or you had a typo in the container ID), the plugin load is never resolved and the processing of the ga() queue is halted.

This, in turn, means that any Google Analytics tags firing after the Optimize container tag has fired will fail to execute. Because the plugin is never loaded, the queue is perpetually waiting for the plugin load to resolve.

You can see this by using the Google Analytics Debugger extension again. In the console, you’ll see something like this:

How to fix this?

Best way to fix this is to avoid deleting Optimize containers when they are no longer in use. That way you’ll avoid stalling the site.

Next, make sure you’ve got the container ID correctly written in the tag.

Finally, a good practice for site load as well is to only load the Optimize container on pages where you are running experiments. It doesn’t make sense to introduce the extra load on pages where the container is not used.

Summary

For all the good that Google Tag Manager’s debug mode does, it does leave something to be desired in terms of verbosity and identifying where the problem lies.

Native templates especially are black boxes. There’s no way to know why a Google Analytics tag signals completion or timeout without trial-and-error testing.

With the instructions provided in this article, you should be able to debug and fix those pesky “Still running” problems you might have with your tags.

Let me know in the comments if you run across a use case not covered by this article. I’ll happily update the text with kudos to you for helping to identify an unexplored problem case!