Files
our-claude-skills/custom-skills/60-gtm-audit/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

327 lines
11 KiB
Markdown

# 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**:
```html
<!-- 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**:
```javascript
// Wrong - GTM loads before dataLayer exists
<script>GTM snippet</script>
dataLayer.push({...});
```
**Fix**:
```javascript
// Correct - Initialize dataLayer first
<script>window.dataLayer = window.dataLayer || [];</script>
<script>GTM snippet</script>
```
### Case Sensitivity Issues
**Symptoms**: Triggers not matching
**Example**:
```javascript
// 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**:
```javascript
// 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**:
```javascript
// 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**:
1. Verify consent mode implementation
2. Check default consent state
3. 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:
```javascript
// 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