Why does my GTM dataLayer variable read as undefined even though I pushed a value before the tag fires?
A GTM Data Layer Variable only sees what is in dataLayer at the exact moment its consuming tag's trigger evaluates. If your push happens asynchronously (after an API call, a click handler, a framework re-render) and the trigger fires on a synchronous event that runs first, GTM reads the variable before your push exists. Fire the tag on the custom event you push, not on a page-level event that races ahead of it.
Why this happens
dataLayer.push() is synchronous JavaScript, but the code that produces the value you are pushing (an API response, a computed cart total, a React state update) is very often asynchronous. GTM evaluates a Data Layer Variable at trigger time by reading the current state of the dataLayer array's merged object - it does not wait for a future push. If your trigger is something page-load-adjacent (gtm.js, DOM Ready, Window Loaded) and your value is pushed later by an async process, the trigger runs first, reads undefined, and the tag fires with a blank parameter.
Fix it
- Never trigger a tag on a page-level dataLayer event when the value it needs comes from a later push. Instead push a dedicated custom event alongside the data:
dataLayer.push({event:"cart_ready", cart_value: 129.00});- the tag should fire oncart_ready, not on DOM Ready. - In GTM, create a Custom Event trigger with Event name
cart_ready(exact match), and set the consuming tag's trigger to that, not to a page-level trigger. - Create the Data Layer Variable (Variables > New > Data Layer Variable) with the exact key name used in the push object, e.g.
cart_value. Key names are case-sensitive and must match the pushed property exactly. - If the value genuinely is not known until after page-level events already fired (common with React/SPA state), push the event only once the value is actually available - inside the callback of the API call or the effect that runs after the state updates.
- For third-party scripts you do not control that push asynchronously, fire the tag on the custom event the third party pushes (check their docs for the event name) rather than guessing at timing with a fixed delay, which reintroduces the same race condition.
How to verify it worked
Open GTM Preview, trigger the action that produces the value (add to cart, complete a step), and in Preview's Data Layer tab confirm the push actually contains the key/value pair at the moment your custom event fires - click the event in the timeline and inspect the Data Layer panel below it. Then click the tag that should have fired on that event and confirm its variable resolved to the real value, not undefined, in the Variables tab of that tag's firing record.
Still stuck after working through this?
Send us what you are seeing in Preview and the Network tab. We trace GTM containers for a living and can usually tell you what is actually happening in one look.
Ask a GTM Engineer