Every analytics team has a moment like this: marketing swears the Shopify dashboard shows 412 orders, finance pulls Stripe and counts 412 orders, and then someone opens GA4 and sees 824. Or 206. Or — most painfully — 412 but with $180,000 of revenue where Shopify reported $95,000. No one touched a tag. No one shipped a deploy. The number is just wrong.
Nine times out of ten, the bug isn't inside GA4. It isn't inside GTM either. It lives in the gap between them — in the contract that the two products assume about each other, a contract that nobody formally defines and nobody formally tests. You can audit your GA4 property and pass every check. You can audit your GTM container and pass every check. And your revenue report can still be off by 2×, because each audit only knows about its own product.
Cross-checks: the 6th chapter of your audit
NiceLookingData runs 5 GA4 × GTM cross-checks automatically when both audits exist in your workspace — measurement ID drift, key event coverage, duplicate tag firing, container bloat, and audit staleness. Included on every plan. See a sample report →
Why single-product audits miss the most expensive bugs
A GA4 audit inspects what GA4 sees. Did events arrive? Are conversions configured? Is consent mode on? It can tell you your purchase event is configured as a key event, that it received 412 events yesterday, and that 100% of them had a value parameter. Clean report. All green.
A GTM audit inspects what GTM is shipping. Are tags firing? Any Custom HTML without consent guards? Any personal Gmail accounts with publish rights? It can tell you you have three GA4 Event tags for purchase, all active, all firing on the checkout-success trigger. Clean report. All green.
And your revenue is still 2× reality, because the two reports are describing the same failure from opposite sides and neither can see it alone. GA4 sees "412 purchases arrived." GTM sees "3 purchase tags are firing." The bug — that those 3 tags each fire on every real checkout, so 412 real purchases become 412 recorded purchases but with triple revenue — only exists in the relationship between them.
The 5 cross-checks every GA4/GTM stack should pass
CROSS-001 · Measurement ID drift
The highest-impact failure mode in the stack. Someone spins up a new GA4 property for a relaunch, updates the Google Tag in GTM to point at the new G-XXXX, and ships the container. What they forget: three separate GA4 Event tags deeper in the container are still hardcoded to the old measurement ID. Those tags keep firing. The tags' debug panel in GTM keeps saying "Tag fired successfully." And GA4 — the new property — receives nothing from them, silently, forever.
The cross-check is simple: extract every measurement ID referenced anywhere in the GTM container, compare against the GA4 property you're auditing, flag any mismatch. You cannot catch this from inside either product. GTM thinks its tags are healthy. GA4 thinks it has no data problem — it just has no data.
How to pass it: store your measurement ID as a Constant variable in GTM (e.g. {{C - GA4 Measurement ID}}) and reference that variable everywhere instead of hardcoding the string. Then a property migration is exactly one change.
CROSS-002 · Key event coverage
You mark generate_lead as a key event in GA4. You import it as a conversion action in Google Ads. You point your Smart Bidding strategy at it. And then nothing — no conversions, no optimization, no lift. Three weeks later someone realizes nobody ever created a GTM tag to fire generate_lead. The event simply doesn't exist in your tag stack.
A close cousin: the event exists, but named wrong. Someone created a GTM tag with event name "Generate Lead" (space, title case), and GA4 happily records it, but the key event that Ads is importing is generate_lead (snake case). Two different events. Ads never sees the one that's firing.
The cross-check walks every key event in GA4 and looks for a matching GTM tag with the identical event name. Gaps and near-matches both flag.
How to pass it: adopt the GA4 recommended-event naming convention — snake_case, verbs — and make it a reviewable rule in your GTM tag naming template. Rename any tags whose event name doesn't match their GA4 key event exactly.
CROSS-003 · Duplicate tag firing
The revenue-doubling bug. Two different developers, over two different sprints, each create a GA4 Event tag for purchase — one named "GA4 · Purchase" and one named "GA4 - ECOMM - Purchase". Both listen on the same trigger (or triggers that both fire on /checkout/success). Every real purchase fires both tags. GA4 records two events. Revenue, purchase count, and conversion rate all inflate by 2×.
This one is especially insidious because GA4 alone cannot tell you "you have two tags firing." GA4 sees events, not tags. It sees "this pageview produced two purchase events" and has no opinion about whether that's correct — some checkouts really do complete two orders. Only GTM knows how many tags are firing. Only the cross-check knows that too many of them are firing for the same event.
How to pass it: inventory your purchase-class tags quarterly. Pause the duplicate, publish the container, validate in GA4 DebugView that purchase fires exactly once per checkout. Expect a ~50% drop in reported purchases the day you deploy the fix — that's the correct number. Update any Ads campaigns that were optimizing against the inflated volume.
CROSS-004 · Container size guardrail
Above a certain size, a GTM container stops being reliably analyzable. At ~500 tags, load performance degrades meaningfully on low-end mobile devices. At ~1,000 tags, even the GTM UI starts to struggle, and static cross-analysis against a GA4 schema gets lossy — we simply can't diff tag→event coverage with high confidence at that scale.
This is a soft check — a warning, not a failure. It exists so that cross-analysis results come with honest confidence bounds. If the check fires, your cross-checks on that container will carry a "best-effort" caveat.
How to pass it: run the standalone GTM audit for cleanup guidance. Typical wins: 15–20 orphaned tags, 5–10 duplicate triggers, a batch of custom variables that were replaced by Built-in Variables years ago and never deleted.
CROSS-005 · GTM audit staleness
Cross-checks compare your live GA4 property against the last-audited snapshot of your GTM container. If the GTM audit is three months old, your cross-checks are three months old — they're still describing tags that were paused, triggers that were renamed, a measurement ID that was fixed two publishes ago. You'll see false positives. Worse, you'll see false negatives — the check that would have caught today's newly-introduced duplicate purchase tag can't see today's container yet.
We flag any paired GTM audit older than 30 days. This is a freshness guarantee, not a failure — the cross-checks still run, but they come with a "re-audit GTM for higher-confidence results" nudge.
How to pass it: re-audit GTM monthly. On Pro, schedule it to run weekly — or always re-run right after a container publish.
Why these five? The gap-mapping logic
We deliberately kept the cross-check set small and unambiguous. Every rule meets three criteria:
- The signal only exists in the intersection. A single-product audit literally cannot detect it. Measurement ID drift, duplicate tag firing, key event coverage gaps — each of these requires simultaneous visibility into both GA4 and GTM.
- High financial or compliance consequence. Every one of these bugs corrupts a number that a CFO, a media buyer, or a compliance officer actually reads. Inflated purchases break Smart Bidding. Missing key events starve conversion campaigns. ID drift makes properties silently dark.
- Unambiguous to detect and unambiguous to fix. No statistical guessing. Either the IDs match or they don't. Either a key event has a matching tag or it doesn't. The fix instruction is one sentence long.
What's deliberately not in the cross-check set: attribution model consistency between GA4 and Ads, session-stitching quirks across cross-domain setups, and anything involving consent-mode behavioral modeling. Those are real problems, but they're probabilistic — the honest answer is "it depends," and a red/green check would mislead more than it helps.
How to run these on your own stack
If you use NiceLookingData: the cross-checks run automatically. Audit GA4, then audit GTM (or the other way around), and the moment both audits exist in your workspace, your next GA4 audit grows a 6th "Integration" chapter with all 5 cross-checks. It's included on Free, Pro, and Agency — there's no gate. The goal is that cross-checks become the baseline, not a premium tier.
If you're running this manually in your own tooling: the cheapest version is a quarterly checklist. Once every three months, export your GTM container, pull the list of GA4 Event tag configurations, and diff against the key events in your GA4 property. You can do this in a spreadsheet in an afternoon. It won't catch drift as it happens — but it will catch the ones that have been silently corrupting your data for weeks.
The single most valuable habit isn't which tool you use — it's treating GA4 and GTM as one system with a testable contract between them, not two independent products you happen to use together. The bugs live in the contract.
Run cross-checks on your own stack
Connect GA4 and GTM, get all 5 cross-checks plus 111 product-level checks in 60 seconds. Free, no credit card.
Run my free audit →Check your GTM container.
Upload your GTM export or connect live. Our auditor checks 53 best practices and gives you actionable fixes.
