Why Old Values Persist in the GTM Data Layer: Merge, Clear and Callbacks
A worked example of why stale values survive in the GTM data model, how null, undefined and ecommerce: null clear them, and how eventCallback timing works.
6 min readBranislav Mateas
Copy post as Markdown
On this page
A tag fires on the right event, but one parameter carries a value from an earlier push. The page code looks correct. The cause is usually not the latest push. It is the data model that Google Tag Manager builds from all earlier pushes on the same page.
This article walks through one controlled example of that behaviour, the ways to clear a value, and how eventCallback and eventTimeout fit into the same queue. It assumes the basics of dataLayer.push() and Version 2 Data Layer Variables, which the data layer guide for analysts covers.
The merge rules behind stale values
The dataLayer array is only a queue of messages. GTM keeps a separate model that holds the latest value for every key. Google's data layer guide states that pushing an existing name overwrites the old value, and that values persist only while the visitor stays on the current page.
Overwriting is the simple case. For nested values, Google's open-source data-layer-helper library documents a fuller rule set. It sorts values into arrays, plain objects and everything else:
In that library, null and undefined count as "everything else". The README does not mention GTM. The claim that GTM's model follows the same rules is an inference. It is supported by Google's GA4 ecommerce samples, covered below, which only make sense if an old ecommerce object would otherwise survive. Confirm the behaviour in your own container with the check in the fourth section.
A worked example: the product that kept a discount
This example is hypothetical. A shop lists two products, then the visitor opens a third one. The code was not run for this article. The results below apply the documented rules by hand.
After push 2, the merged ecommerce value works out as follows:
Key
Value after push 2
Why
currency, value
EUR, 40
New keys, added
item_list_name
Search results
Not in push 2, kept
items[0]
SKU-C, Kettle, price 40, discount 2
Objects merged; discount kept from Tea
items[1]
SKU-B, Mug, price 8
Push 2 has no index 1, kept
If a GA4 tag reads the ecommerce object from the data layer at view_item, it can send two items instead of one. The item prices then sum to 40 + 8 = 48, while the event value says 40. The kettle also carries a 2 euro discount that belongs to the tea, and the event keeps a list name from a different page state.
Push 2 contained only correct data. The wrong values come from push 1. This is why reading the code of the latest push alone does not explain the hit.
Clearing values: null, undefined, empty arrays and reset
Google's GA4 ecommerce guide for Tag Manager puts this line before each ecommerce push in its samples: dataLayer.push({ ecommerce: null }); with the comment "Clear the previous ecommerce object." Under the merge rules, null is not a plain object, so it replaces the whole object. The next push then finds null and replaces it with a fresh object. The merge never happens.
Applied to the example, a null push between push 1 and push 2 leaves exactly one item, SKU-C, with no discount and no list name. The item total then matches the event value: 40.
Other ways to clear behave differently:
A key set to undefined or null. Either one replaces that single key, for example ecommerce: { coupon: undefined }. The rest of the object stays merged. Tags may treat the two values differently, so check what the variable returns in Preview.
An empty array. By the library's rules, items: [] has no indexes to merge, so the old items stay. This is an inference from the documented index rule. Do not rely on an empty array to clear a list.
_clear: true. The data-layer-helper README documents this flag to stop a merge. Google's GTM documentation does not mention it, so it is not a safe choice for a GTM setup unless your own tests confirm it.
this.reset(). The data layer guide documents a reset method that clears the whole model. It suits pages that stay open for a long time. It also removes page context such as page type or user state, so those keys must be pushed again.
Persistence applies to every key, not only ecommerce. If a form pushes error_field: 'email' on a failed submit, a later success event on the same page still sees email unless the success push sets error_field to undefined. A full page load clears the model. A single-page app route change does not.
Checking the model yourself
Two checks show what GTM actually holds. Neither needs a container change.
First, open Tag Assistant and select the event in question. Google's Tag Assistant guide says the Data Layer tab shows both the data pushed for each event and the current state of the data layer. Compare the pushed message with the state. A key that appears in the state but not in the message has carried over.
Second, read the model from the browser console with the get method from the data layer guide. Run it right after the event you suspect:
Function pushes are queued like other messages, so the log reflects the model after all earlier messages. Expected result for the example without a null push: two items, with discount: 2 on SKU-C. If the container shows something else, its rules differ from the library's, and its behaviour is what counts.
eventCallback and eventTimeout
The same queue explains callback timing. A push can include eventCallback, a function GTM runs after it finishes the tags for that event. eventTimeout sets a time in milliseconds after which the callback runs anyway.
Google's current GTM data layer guide does not document these two keys. The gtag.js parameter reference documents the equivalents, event_callback and event_timeout. For the GTM keys, Simo Ahava's tip on eventTimeout (2016, updated 2018) describes the same behaviour and its main risk:
if (window.google_tag_manager) {
dataLayer.push({
event: 'outbound_click',
eventCallback: function () { window.location = url; }, // url: link target
eventTimeout: 2000
});
}
Without the timeout, a tag that hangs or never reports completion stops the callback, so the redirect never happens. The google_tag_manager check matters too. If GTM is blocked, nothing processes the push and the callback never runs. That tip advises against delaying navigation through GTM for this reason. A form or link that works without the callback is the safer pattern, with the callback used only for non-essential follow-up work.
The callback does not reset the model either. Values pushed alongside it merge in like any other keys and stay until a later push replaces them.