Files
our-claude-skills/custom-skills/61-gtm-manager/code/docs/common_issues.md
Andrew Yim 1eaec93c13 docs(gtm): add subdomain regex dot separator gotcha
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>
2026-03-21 02:05:27 +09:00

11 KiB

Common GTM Issues & Fixes

Container Issues

GTM Not Firing

Symptoms: No GTM requests in network tab Causes:

  1. Script blocked by ad blocker
  2. Script placed after closing body tag
  3. JavaScript error before GTM loads
  4. 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:

  1. Legacy container not removed
  2. Different teams installed separate containers
  3. Theme/plugin auto-installed GTM

Fix:

  1. Audit all containers in source
  2. Consolidate to single container
  3. Use GTM environments for staging/prod

Container ID Mismatch

Symptoms: Tags not firing, wrong property receiving data Causes:

  1. Dev/staging container on production
  2. 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:

  1. ✓ Trigger conditions met?
  2. ✓ Trigger enabled?
  3. ✓ Tag not paused?
  4. ✓ No blocking triggers active?
  5. ✓ Consent mode not blocking?

Debug Steps:

  1. GTM Preview > Check Tags Fired
  2. Verify trigger shows green check
  3. Check Variables tab for expected values

Duplicate Tag Firing

Symptoms: Events counted 2x in GA4 Causes:

  1. Multiple triggers on same action
  2. Page re-renders triggering again
  3. SPA virtual pageviews firing multiple times

Fix:

  1. Add "Once per event" tag firing option
  2. Use trigger groups to control firing
  3. Add conditions to prevent re-firing

Wrong Parameters Sent

Symptoms: Data appears in wrong fields in GA4 Debug:

  1. GTM Preview > Tags > Show fired tag
  2. Check "Values" sent with tag
  3. 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:

  1. Tax/shipping included inconsistently
  2. Currency conversion issues
  3. Discount applied incorrectly

Purchase Event Fires Multiple Times

Symptoms: Same order tracked 2-3x Causes:

  1. Page refresh on confirmation
  2. Browser back button
  3. Email link revisit

Fix:

// Check if already tracked
if (!sessionStorage.getItem('purchase_' + transaction_id)) {
  dataLayer.push({ event: 'purchase', ... });
  sessionStorage.setItem('purchase_' + transaction_id, 'true');
}

Symptoms: Tags show "Blocked by consent" in Preview Fix:

  1. Verify consent mode implementation
  2. Check default consent state
  3. Test with consent granted

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:

  1. Page Path based (e.g., (\/m)?\/specialEvent\/(.*)) — for internal reports
  2. 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:

  1. Too many synchronous tags
  2. Large third-party scripts
  3. Tags in wrong firing sequence

Fix:

  1. Use tag sequencing
  2. Load non-critical tags on Window Loaded
  3. Defer marketing tags