The pattern (www|app|...|\.)?josunhotel does NOT match www.josunhotel.com. Fix: (www|app|...)?\.?josunhotel moves the dot outside the alternation as a separate optional match. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
11 KiB
Common GTM Issues & Fixes
Container Issues
GTM Not Firing
Symptoms: No GTM requests in network tab Causes:
- Script blocked by ad blocker
- Script placed after closing body tag
- JavaScript error before GTM loads
- Consent management blocking GTM
Fix:
<!-- Place immediately after opening <head> tag -->
<script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
Multiple Containers Conflict
Symptoms: Duplicate events, inconsistent data Causes:
- Legacy container not removed
- Different teams installed separate containers
- Theme/plugin auto-installed GTM
Fix:
- Audit all containers in source
- Consolidate to single container
- Use GTM environments for staging/prod
Container ID Mismatch
Symptoms: Tags not firing, wrong property receiving data Causes:
- Dev/staging container on production
- Copy-paste error during installation
Fix: Verify container ID matches GTM account
DataLayer Issues
DataLayer Not Initialized
Symptoms: First push events lost Code Error:
// Wrong - GTM loads before dataLayer exists
<script>GTM snippet</script>
dataLayer.push({...});
Fix:
// Correct - Initialize dataLayer first
<script>window.dataLayer = window.dataLayer || [];</script>
<script>GTM snippet</script>
Case Sensitivity Issues
Symptoms: Triggers not matching Example:
// DataLayer pushes "AddToCart"
dataLayer.push({ event: "AddToCart" });
// But GTM trigger looks for "addToCart" - won't match!
Fix: Standardize event naming (recommend lowercase with underscores)
Wrong Data Types
Symptoms: Calculations wrong in GA4, missing data Example:
// Wrong - price as string
dataLayer.push({ ecommerce: { value: "29.99" }});
// Correct - price as number
dataLayer.push({ ecommerce: { value: 29.99 }});
Timing Issues
Symptoms: Events fire before data available Cause: DataLayer push happens after tag fires
Fix: Use "Custom Event" trigger instead of "Page View"
Tag Issues
Tag Not Firing
Checklist:
- ✓ Trigger conditions met?
- ✓ Trigger enabled?
- ✓ Tag not paused?
- ✓ No blocking triggers active?
- ✓ Consent mode not blocking?
Debug Steps:
- GTM Preview > Check Tags Fired
- Verify trigger shows green check
- Check Variables tab for expected values
Duplicate Tag Firing
Symptoms: Events counted 2x in GA4 Causes:
- Multiple triggers on same action
- Page re-renders triggering again
- SPA virtual pageviews firing multiple times
Fix:
- Add "Once per event" tag firing option
- Use trigger groups to control firing
- Add conditions to prevent re-firing
Wrong Parameters Sent
Symptoms: Data appears in wrong fields in GA4 Debug:
- GTM Preview > Tags > Show fired tag
- Check "Values" sent with tag
- Compare with expected parameters
E-commerce Issues
Missing Transaction ID
Symptoms: Duplicate purchases counted
Fix: Ensure unique transaction_id generated server-side
Items Array Empty
Symptoms: Revenue tracked but no products
Check: ecommerce.items array populated
Value Mismatch
Symptoms: Revenue doesn't match actual Causes:
- Tax/shipping included inconsistently
- Currency conversion issues
- Discount applied incorrectly
Purchase Event Fires Multiple Times
Symptoms: Same order tracked 2-3x Causes:
- Page refresh on confirmation
- Browser back button
- Email link revisit
Fix:
// Check if already tracked
if (!sessionStorage.getItem('purchase_' + transaction_id)) {
dataLayer.push({ event: 'purchase', ... });
sessionStorage.setItem('purchase_' + transaction_id, 'true');
}
Consent Mode Issues
Tags Blocked by Consent
Symptoms: Tags show "Blocked by consent" in Preview Fix:
- Verify consent mode implementation
- Check default consent state
- Test with consent granted
Consent Not Updating
Symptoms: Tags stay blocked after user accepts
Fix: Verify gtag('consent', 'update', {...}) fires on accept
SPA (Single Page App) Issues
Pageviews Not Tracking Navigation
Symptoms: Only initial pageview tracked Cause: No page reload on route change
Fix: Implement History Change trigger or custom event:
// On route change
dataLayer.push({
event: 'virtual_pageview',
page_path: newPath,
page_title: newTitle
});
Events Fire on Old Page Data
Symptoms: Wrong page_path in events Fix: Update page variables before event push
Content Grouping Issues (RegEx Table Variables)
How Content Grouping Works in GTM
Content grouping uses RegEx Table variables to categorize pages by URL/path patterns.
The variable takes an input (typically {{Page Path}} or {{Page URL}}), tests it against
an ordered list of regex patterns, and returns the first matching output value.
If no pattern matches, it falls to the Default Value (usually "Other").
Key settings:
- Full Matches Only: OFF — partial regex match is sufficient
- Capture Groups and Replace: OFF — output is the literal value, not a regex replacement
- Ignore Case: ON — case-insensitive matching
New Page Paths Fall Through to "Other"
Symptoms: New sections (campaigns, special events, landing pages) show as "Other" in GA4 content group reports.
Root cause: The RegEx Table was built for the site's original URL structure. New paths like /specialEvent/, /campaign/, /timesale/ don't match any existing pattern.
Example: JHR site has /specialEvent/landersshoppingfesta2026/teaser.do but only /package/ and /event/ are in the regex table, not /specialEvent/.
Fix: Add regex rule for the new path pattern. Place it BEFORE the default.
Gotcha — two variables to update: Sites often have parallel content group variables:
- Page Path based (e.g.,
(\/m)?\/specialEvent\/(.*)) — for internal reports - Page URL based (e.g., full URL with domain regex) — for cross-domain or subdomain tracking
Both must be updated or they'll produce inconsistent groupings.
RegEx Pattern Conventions (JHR Example)
Page Path patterns follow this structure:
(\/m)?\/section\/(.*) → Korean Category Label
(\/m)?— optional mobile path prefix\/section\/— the URL path segment to match(.*)— match anything after
Page URL patterns include domain matching:
^https\:\/\/(www|app|jpg|gjb|gjj|grp|\.)?josunhotel\.com(\/m)?\/section\/(.*)
- Multi-subdomain support via alternation group
- Same
(\/m)?mobile prefix handling
Content Group Categories (Hospitality Reference)
| Pattern | Category (KR) | Category (EN) |
|---|---|---|
/intro.do, /main.do |
메인페이지 | Main Page |
/about/, /press/ |
기업 소개 | Corporate Info |
/hotel/ |
호텔 소개 | Hotel Info |
/rooms/ |
객실 소개 | Room Info |
/dining/ |
다이닝 이용 | Dining |
/facilities/, /meeting/, /fitness/ |
부대시설 소개 | Facilities |
/package/, /event/ |
이벤트 | 패키지 조회 | Events & Packages |
/specialEvent/ |
캠페인 | 프로모션 | Campaign & Promotion |
/timesale/, /campaign/ (placeholder — not actual paths yet, replace when specific paths are registered) |
캠페인 | 프로모션 | Campaign & Promotion |
/activity/ |
액티비티 소개 | Activities |
/step[0-4] |
룸, 패키지 예약 프로세스 | Booking Process |
/resve/dining/ |
다이닝 예약 프로세스 | Dining Booking |
/membership/ |
멤버쉽 | Membership |
/mypage/, /login/, /join/ |
웹회원 관련 | Member Portal |
/retail/, /product/, /office/ |
리테일 관련 | Retail |
/voc/, /customer/ |
고객지원 | Customer Support |
/policy/ |
웹사이트 정책 | Website Policy |
/upload_file/event/html/ |
캠페인 | 프로모션 | Campaign & Promotion |
| Default | Other | Other |
Two Content Group Variables — Old vs New (JHR)
The JHR container has two content group variables for menu categories:
| Contents Group 2 (ID:1234) | Contents Group new (ID:1826) | |
|---|---|---|
| Input | {{Page Path}} |
{{Page URL}} |
| Rules | 20 | 24 (replacement) |
| Domain matching | No — path only | Yes — full URL with subdomain alternation |
| Subdomain coverage | N/A | www|app|jpg|gjb|gjj|grp|les|\. |
| Cross-domain | No | Yes — booking, josuntns, lounge subdomains |
| Mobile path | (\/m)? prefix |
(\/m)? prefix within URL pattern |
"Contents Group new" (ID:1826) is the replacement — it handles subdomains (booking, T&S, lounge, les) that the old path-based variable cannot distinguish. When auditing, check which variable is referenced in the GA4 config tag — the newer one should be used.
Gotcha — adding new subdomains: When a new hotel brand subdomain launches (e.g., les.josunhotel.com), the subdomain alternation group (www|app|...|les|\.) in ALL main-domain rules must be updated. This affects 14+ rules in the URL-based variable.
Gotcha — Subdomain Regex Dot Separator
Bug: (www|app|jpg|les|\.)?josunhotel\.com does NOT match www.josunhotel.com.
The \. inside the alternation group matches a literal dot OR www matches www, but neither consumes the . between the subdomain and josunhotel. The regex engine sees www then expects josunhotel but finds .josunhotel.
Fix: Move the dot outside the group as an optional separator:
BROKEN: (www|app|jpg|gjb|gjj|grp|les|\.)?josunhotel\.com
FIXED: (www|app|jpg|gjb|gjj|grp|les)?\.?josunhotel\.com
This correctly matches: www.josunhotel.com, les.josunhotel.com, josunhotel.com (bare), app.josunhotel.com.
Note: GTM's "Full Matches Only = OFF" means re.search (partial match) is used, which may accidentally match the broken pattern on some URLs. But strict re.match or JavaScript pattern.test() will fail. Always test with the full URL in JavaScript console.
Audit Checklist for Content Grouping
- Run
dtm var info <var_id>to dump the full regex table - Check if new site sections have matching patterns
- Verify both Path and URL variables are in sync
- Check GA4 config tag references the correct variable
- Test in GTM Preview: navigate to new page, check variable value
- After update, verify in GA4 Realtime > Content Group report
Performance Issues
Tags Slowing Page Load
Symptoms: High LCP, slow TTI Causes:
- Too many synchronous tags
- Large third-party scripts
- Tags in wrong firing sequence
Fix:
- Use tag sequencing
- Load non-critical tags on Window Loaded
- Defer marketing tags