Debugging Journal
Real, specific, root-caused bugs — not generic troubleshooting tips. 18 entries, presented as Symptom → Root Cause → Fix. Several of these are also distributed to their relevant goal pages.
CSP whack-a-mole pattern
Symptom
A newly-added tracking platform’s requests silently fail — no visible error unless DevTools Console is checked directly.
Root Cause
Every new tracking platform (Google Ads native conversion, YouTube’s IFrame API, Meta’s Pixel, the server-side subdomain) requires a corresponding new Content Security Policy allowlist entry — script-src, connect-src, img-src, frame-src — each time. CSP violations block network requests with no obvious error.
Fix
Treat the Consolidated CSP Reference as a starting checklist for any new client site, rather than rediscovering each domain one error at a time.
Consent-gated tags that never re-evaluate
Symptom
A tag gated by an Additional Consent Check, triggered only by Initialization - All Pages, never fires — even after the visitor accepts consent.
Root Cause
The tag evaluates exactly once, before consent exists by default, and never gets a second chance once consent is later granted.
Fix
Add a second trigger tied to a custom consent_update event so the tag re-evaluates whenever consent state actually changes. Applied to Microsoft Clarity, LinkedIn Insight Tag, and Meta Pixel Base — all page-load, one-time-evaluation tags.
The same fix, wrongly applied
Symptom
Call Click and WhatsApp Click’s Meta tags fired the instant any visitor accepted the cookie banner, with zero actual clicks.
Root Cause
The Consent Update retry-trigger fix from #2 was mistakenly also applied to these tags — which are event-driven (fire fresh on every real click), not page-load tags with a stale-evaluation problem.
Fix
The retry-trigger fix only applies to tags whose sole trigger is a one-time page-load event, never to already-event-driven tags. This exact mistake recurred once more later in the build — see #13.
AND vs. OR trigger logic
Symptom
WhatsApp click tracking needed to match either wa.me or web.whatsapp.com URLs — built as a single trigger with two conditions, it silently never fired.
Root Cause
A single trigger with two conditions produces AND logic (both must be true simultaneously) — impossible for one URL to satisfy both.
Fix
The correct GTM pattern for OR logic is two separate single-condition triggers, both attached to the same tag.
Variable typed as text instead of selected
Symptom
A Conversion Label value sent garbage data with zero errors thrown.
Root Cause
The value was manually typed into a field as {{Variable Name}} literal text rather than actually selected via GTM’s variable picker — visually identical in some views, but GTM treats it as a literal unresolved string.
Fix
Always select variables via the picker, never type the {{...}} syntax by hand.
Enhanced Conversions’ “Include user-provided data” field removed without notice
Symptom
A previously-documented Conversion Tracking tag field for Enhanced Conversions no longer exists in the tag editor.
Root Cause
Google removed this option from the client-side Conversion Tracking tag in April 2025. Official documentation continued describing it as available for some time afterward — cost real debugging time before a recent, dated practitioner report clarified the deprecation.
Fix
The current, correct method is a separate, dedicated Google Ads: User-provided Data Event tag.
A known GTM Preview false-negative
Symptom
The native Google Ads Conversion Tracking tag type shows “Failed” in GTM’s own Preview mode.
Root Cause
A documented tendency of this specific tag type to misreport in Preview even when the conversion genuinely succeeds server-side.
Fix
Confirmed via direct network-request inspection that correct data reaches Google’s servers despite the misleading debug label. Don’t chase this specific tag type’s “Failed” status without corroborating evidence from Google Ads’ own side first.
A sessionStorage dedup bug affecting every platform simultaneously
Symptom
A second, genuinely distinct lead submitted within the same browser session silently produced zero tracking events across every platform.
Root Cause
The thank-you page’s duplicate-prevention guard used a session-wide boolean flag (“has this browser session tracked any lead, ever”) instead of comparing against the specific lead’s unique token.
Fix
Compare against the specific gate token last tracked, not a blanket boolean — a refresh of the same page still correctly deduplicates; a genuinely new submission (a new token) does not get blocked.
Meta’s dataset-creation dialog silently opts into managed Conversions API
Symptom
A duplicate, non-consent-gated CAPI system was found running in parallel to the deliberately-built server-side GTM architecture.
Root Cause
A checkbox (“Add Conversions API for web events to all new and existing datasets”) is checked by default when creating a new Meta Pixel/dataset — this auto-enrolls the Pixel into Meta’s own fully-managed, black-box CAPI system.
Fix
Manually find and disconnect this via the dataset’s own Conversions API settings (Business Settings → Data Sources → Conversions API connection → toggle off Automatically connect + Remove the connection).
LinkedIn requires an always-present (even if empty) field
Symptom
LinkedIn’s Conversions API returns a hard 422 error for events with no real identifiers.
Root Cause
user.userIds being omitted entirely from the payload — even when no real identifiers exist for that specific event.
Fix
The field must be present as an empty array, never omitted. This is the offline Worker’s direct LinkedIn Conversions API integration, not the online GTM-based Insight Tag — see the Offline Conversion Loop infrastructure page.
A Google Sheets tab name with special characters broke A1-notation range references
Symptom
A backend integration reading/writing the leads Sheet failed silently on range references.
Root Cause
The integration assumed a plain tab name (Leads), but the real tab name (Digital Asur — Leads, containing an em dash and spaces) requires the tab name to be wrapped in single quotes within any API range reference.
Fix
Wrap tab names containing spaces or special characters in single quotes for every A1-notation range reference.
An inconsistent “skip if no data” check across two near-identical integrations
Symptom
Rows with genuinely no identifying data (an expected scenario given an optional email field) showed up as LinkedIn failures rather than clean, expected skips.
Root Cause
Meta’s CAPI integration correctly pre-checked for the presence of at least one identifier before attempting a push — the equivalent LinkedIn integration lacked this same check.
Fix
Added the matching pre-check to the LinkedIn push path: skip cleanly, increment a dedicated counter, and never attempt (or count as a failure) a push for a row with zero identifiers.
A tag missing consent gating entirely, only discovered by cross-referencing real GTM exports against this document
Symptom
LinkedIn - Conversion (Generate Lead) had zero consent gating configured, meaning it would fire regardless of visitor consent. Not caught during the original build’s testing.
Root Cause
Surfaced only when a separate AI session, given the raw container export as ground truth, checked every tag’s actual consent configuration systematically rather than trusting the accompanying documentation’s claims.
Fix
Confirmed live via direct testing (declined consent → submitted a lead → tag fired anyway), then fixed with an Additional Consent Check requiring ad_storage + ad_user_data. A follow-up round then found the fix had also attached a Consent Update retry-trigger to this same event-driven tag — recurring the exact mistake from #3 on a different platform. That trigger was removed in a later round, confirmed via the export showing exactly one firing trigger (Generate Lead Event) remaining.
A live secret exposed via a routine GTM container export
Symptom
A real, live Meta CAPI access token was found in plaintext inside a shared container export file.
Root Cause
Exporting a GTM container as JSON includes every Constant variable’s literal value, including secrets like access tokens — regardless of which container (Web vs. Server) the secret lives in.
Fix
Treat any GTM container export file itself as sensitive if it contains real Constant-type secrets, and rotate any credential that was ever included in a widely-shared export — independent of whether the container placement itself was otherwise correct.
A verification gap that took an unusually long time to resolve, and did so in a genuinely interesting way
Symptom
Multiple well-isolated test attempts across several sessions all came back inconclusive for the Meta CAPI tag.
Root Cause
Real live confirmation finally arrived unprompted — a timestamped Lead event appeared in Meta’s Test Events tool showing "Received from: Server," correlated with a real, simultaneous increase in Stape’s own request counter. The event was marked "Deduplicated" despite showing no visible Event ID — the project never built the explicit event_id-sharing mechanism between the client-side Pixel and server-side CAPI tag, so Meta most likely deduplicated using a fallback signal (the shared _fbp/_fbc browser cookie) instead.
Fix
The CAPI tag is confirmed genuinely working, via an implicit fallback matching method rather than the more precise, Meta-recommended explicit event_id method. See the Event ID Deduplication guide for closing that remaining gap.
Server-side “Automap User Data” silently failed for custom-named parameters
Symptom
Even after confirming user_email/user_phone were genuinely present and correctly populated in the outgoing GA4 event (verified via Tag Assistant, Values mode), Meta’s Test Events tool consistently showed no Email/Phone in that event’s "User data keys."
Root Cause
GTM server-side Event Data variables, when reading GA4-client-parsed events, must reference nested paths (params.user_email), not flat top-level keys (user_email) — GA4’s actual Measurement Protocol structure nests custom event parameters under a params object. Not found in any official documentation or practitioner writeup — identified as a well-reasoned hypothesis, then confirmed correct only by testing it live.
Fix
Built two Event Data variables with keys params.user_email and params.user_phone, then manually added two Property rows in the CAPI tag’s User Data section (Email/Phone), leaving "Automap User Data" checked alongside these explicit overrides, not replacing it. Verified fixed: Meta’s Test Events tool subsequently showed User data keys: Email address, Browser ID, IP address, Phone number, User agent.
A second, distinct GTM editor-time false positive
Symptom
"Cannot detect if the Google tag is in your container" appears in the GTM editor.
Root Cause
Appears specifically when a Conversion ID field references a variable rather than a literal typed string, because GTM’s static editor-time validator cannot resolve variables at edit-time (only at runtime).
Fix
Unrelated to the "Failed" Preview-status false-negative (#7) — both are real, separate, documented quirks worth not chasing as if they were genuine errors, once confirmed benign.
YouTube duplicate-firing diagnosis technique
Symptom
Suspected two independent tracking systems both firing for the same event.
Root Cause
Confirmed by checking the fired event’s own field list for values one system’s code doesn’t reference at all — videoDuration/videoCurrentTime/videoElapsedTime/videoVisible appeared despite the custom-built tracking code never setting any of those fields, directly proving GTM’s native auto-detection was firing independently and simultaneously.
Fix
A useful diagnostic method worth keeping regardless of this specific bug’s outcome (ultimately deferred unresolved — see YouTube Engagement goal page).