Polaris Audit

Case #013 · 12 May 2026 · 6 min read

Race Conditions in GTM: How Tags Fire Before Consent

You configured Consent Mode v2. The tags are gated. The network log still shows GA4 at 180ms. Here is the timing gap that breaks correctly-configured setups.

GTM Consent Mode v2 race conditions are the most common source of unexplained pre-consent fires. A developer follows the documented setup: Consent Mode is initialised, tags are gated on the correct signals, the CMP is wired. Then a forensic scan shows GA4 firing at 180ms, before any consent click was possible. The configuration looks correct. The network proves otherwise. The explanation is a timing gap that the documentation underplays.

TL;DR

  • GTM loads async. The CMP also loads async. There is a window — typically 50–300ms — where GTM has initialised but the CMP has not yet set consent state.
  • In that window, GTM evaluates tag conditions against undefined consent state and fires tags if the default state permits it.
  • The fix is two-part: set a restrictive gtag("consent", "default") before GTM loads, and confirm the CMP calls gtag("consent", "update") fast enough to close the gap.
  • The only way to verify the gap is closed is a live network interception — not a GTM preview, not a CMP dashboard.

Verdict

Consent Mode v2 configuration is necessary but not sufficient. The default consent state and the speed of the CMP’s update call determine whether the race condition fires. Correct configuration does not prevent race conditions — it shifts the problem to timing. Only a network-level test can confirm the gap is closed.

The 300ms gap nobody documents

Both GTM and the CMP are loaded asynchronously. Neither is guaranteed to initialise before the other. In practice, GTM tends to execute faster — it is a single script that evaluates immediately — while CMPs typically involve an additional async fetch to retrieve consent configuration from their CDN.

The result is a window of approximately 50 to 300ms during which GTM has loaded, its container has been evaluated, and tag triggers are being assessed — but the CMP has not yet pushed consent state into the data layer. GTM has no consent signal to read. What it does next depends entirely on the default consent state.

If gtag("consent", "default") was not called with denied for all relevant consent types before GTM loaded, GTM falls back to treating unmapped consent types as granted. Tags fire. The network call goes out. By the time the CMP initialises and sets the correct state, the violation has already occurred.

How GTM reads consent state

When a GTM tag has a consent check configured, GTM does not wait for consent state to arrive. It evaluates the current state of the consent model at the moment the tag’s trigger fires. If consent state is undefined at that moment, GTM uses the default.

The default is controlled by the gtag("consent", "default") call. Google’s own documentation recommends placing this call in a <script> tag immediately before the GTM snippet. The intent is that the default is set synchronously, before GTM initialises, so there is no window where GTM evaluates against undefined state.

The compliance-correct default looks like this:

gtag("consent", "default", {
  ad_storage: "denied",
  analytics_storage: "denied",
  ad_user_data: "denied",
  ad_personalization: "denied",
  wait_for_update: 500
});

The wait_for_update parameter tells GTM to hold for up to 500ms for a consent update before evaluating. This extends the window in which the CMP can push state before GTM fires tags. It is not a guarantee — if the CMP takes longer than 500ms to initialise, the race condition still fires. GTM Consent Mode v2 in full →

Your site is leaking data before consent.

Free headless-browser scan. Catches GA4, Meta Pixel, TikTok and more firing before the click. Results in 10 seconds.

Run a free scan

The three race conditions

Most pre-consent fires from correctly-configured setups trace back to one of three patterns:

  1. Missing default call. The GTM snippet was deployed, Consent Mode was enabled in GTM settings, but the synchronous gtag("consent", "default") call was never added to the page template. GTM uses an implicit granted default for unmapped types.
  2. Slow CMP initialisation. The default is set correctly to denied, but wait_for_update is either absent or set too low. The CMP fetches its config from a slow CDN. GTM times out and evaluates with the default state, which should mean denied — but if the tag is in Basic mode rather than Advanced, it may fire anyway.
  3. Tag operating in Basic Consent Mode.Basic mode only fires the tag after consent is granted. Advanced mode fires the tag in a cookieless state before consent, then re-fires with full data after. Many agencies enable Advanced mode on GA4 without understanding this: GA4 fires immediately at load, even before consent, and sends an uncookied request to Google’s servers. Whether this constitutes a violation is disputed — regulators in Austria and France have found that even cookieless data transmission requires consent.

Default vs update: what each one does

Understanding the relationship between the two Consent Mode commands is essential to diagnosing race conditions:

gtag("consent", "default") sets the initial state before any user interaction. It must run synchronously before the GTM container initialises. If it runs after, it has no effect on the race window.

gtag("consent", "update")is called by the CMP after the user makes a consent decision. It updates the consent state and triggers any tags that were held pending. The CMP must call this — GTM does not call it automatically.

If the CMP is not explicitly integrated to call updateon user interaction, GTM never receives the consent signal even after the user clicks Accept. Tags remain in their default state indefinitely. This is a different failure mode from the race condition — it is a wiring failure rather than a timing failure — but the result in the network is the same: no tags fire after consent, and the user’s consent click has no downstream effect.

How to verify the gap is actually closed

GTM Preview mode does not simulate the race condition. It loads the debugger in a separate context that may initialise differently than a production page. The CMP dashboard confirms that consent configuration exists, not that blocking works under real timing conditions.

The only reliable verification is a headless browser that intercepts all outbound network requests during an unconsented session. Load the page. Do not interact with the banner. Watch what fires in the 0–2000ms window. If google-analytics.com/g/collect appears in that window, the race condition is real and active, regardless of what the configuration looks like.

This is also how regulators verify violations. The DPA investigator does not read GTM config. They read network logs from a clean browser session. A clean network log is the only audit-ready proof. How to read a GTM container audit for GDPR leaks →

See the three specific failure modes for GA4 specifically →

Your site is leaking data before consent.

Paste any URL and see exactly which tags fire in the pre-consent window — no signup, no install, 10 seconds.

Run a free scan

Further Reading

← All posts