Debug & Validation
Comprehensive guide to debugging and validating your analytics implementation.
Debugging Tools
GA4 DebugView
Real-time event stream for debugging:
- Enable Debug Mode:
// Via GTM: Set debug_mode parameter
// In GA4 Configuration tag:
// Configuration Settings → debug_mode = true
// Or via URL parameter:
// ?debug_mode=1
// Or via gtag:
gtag('config', 'G-XXXXXXXXXX', {
debug_mode: true
});
-
Access DebugView:
- GA4 → Admin → DebugView
- Select your device from the dropdown
-
What to Check:
- Events appearing in correct order
- Parameter values populated
- User properties set correctly
- Errors/warnings in event cards
GTM Preview Mode
Most powerful debugging tool for tag implementation:
-
Start Preview:
- Click Preview button in GTM
- Enter your website URL
- Opens Tag Assistant Connected
-
Key Sections:
| Section | Purpose | |---------|---------| | Summary | All tags, fired vs. not fired | | Tags | Individual tag details | | Variables | All variable values | | Data Layer | Data layer contents at each event | | Errors | Any JavaScript errors | | Consent | Consent state (if enabled) |
- Event Analysis:
- Click each event in timeline
- See which tags fired
- Check variable values at that moment
- Verify trigger conditions
Browser DevTools
Network Tab
// Filter for analytics requests:
// - collect? (GA4 Measurement Protocol)
// - google-analytics
// - facebook.com/tr
// - bat.bing.com
Key things to check:
- Request status (200 = success)
- Request payload (parameters sent)
- Request timing
Console Tab
// Enable verbose logging for GA4
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
// Monitor data layer
console.table(dataLayer);
// Watch for changes
(function() {
var oldPush = dataLayer.push;
dataLayer.push = function() {
console.log('dataLayer.push:', arguments[0]);
return oldPush.apply(this, arguments);
};
})();
Google Tag Assistant
Browser extension for real-time validation:
- Install from Chrome Web Store
- Enable on your website
- View all Google tags detected
- Check for issues and warnings
Validation Workflows
New Implementation Checklist
## Pre-Launch Validation
### Basic Functionality
- [ ] Page view fires on all pages
- [ ] Events fire on expected interactions
- [ ] Conversions track correctly
- [ ] Cross-domain tracking works (if applicable)
### Data Quality
- [ ] No PII in parameters
- [ ] Event names follow conventions
- [ ] Parameter values populated
- [ ] No duplicate events
### Consent & Privacy
- [ ] Consent mode configured
- [ ] Tags blocked without consent
- [ ] Tags fire after consent
- [ ] Opt-out works correctly
### Performance
- [ ] Page load not impacted > 100ms
- [ ] No console errors
- [ ] Tags load asynchronously
Event Validation Matrix
| Event | Trigger | Required Params | Test Case | |-------|---------|-----------------|-----------| | page_view | All pages | page_location, page_title | Navigate to any page | | view_item | Product pages | item_id, item_name, price | View a product | | add_to_cart | Add button click | item_id, quantity, value | Add item to cart | | purchase | Thank you page | transaction_id, value, items | Complete purchase |
Common Issues
Tags Not Firing
| Symptom | Diagnosis | Solution | |---------|-----------|----------| | Tag shows "Not Fired" | Check trigger conditions | Fix trigger logic | | Tag fires but no data | Check variables | Fix variable configuration | | Intermittent firing | Race condition | Adjust trigger timing | | No tags at all | GTM not loading | Check GTM snippet |
Data Layer Issues
// Problem: Data layer undefined
// Solution: Initialize before use
window.dataLayer = window.dataLayer || [];
// Problem: Event not triggering tag
// Check event name matches trigger
dataLayer.push({
'event': 'purchase' // Must match GTM trigger exactly
});
// Problem: Variables undefined
// Push data before event
dataLayer.push({
'transaction_id': 'T123',
'value': 99.99,
'event': 'purchase' // Event comes LAST
});
Duplicate Events
Causes:
- Multiple tag instances
- Tag firing on multiple triggers
- SPA route changes
- Misconfigured triggers
Diagnosis:
// Count events in data layer
dataLayer.filter(x => x.event === 'purchase').length
// Should equal actual purchases
// Check in GA4 DebugView
// Look for duplicate event_ids
Solutions:
- Add de-duplication logic
- Use transaction_id for purchase events
- Configure "Once per page" triggers
- Check for multiple GTM containers
Cross-Domain Issues
Symptoms:
- New sessions when crossing domains
- Self-referrals in reports
- Attribution broken across domains
Diagnosis:
// Check linker parameter in URL
// Should see _gl parameter when crossing domains
// https://checkout.example.com?_gl=1*abcdef...
// Check cookie domain
document.cookie // Look for _ga cookie
// Should be on parent domain: .example.com
Solutions:
- Configure domains in GA4 Admin
- Check linker configuration in GTM
- Verify redirect handling
- Test full user journey
Testing Frameworks
E2E Analytics Testing
Using Playwright or Cypress:
// Playwright example
test('purchase tracking', async ({ page }) => {
// Intercept analytics requests
const analyticsRequests = [];
page.on('request', request => {
if (request.url().includes('google-analytics')) {
analyticsRequests.push(request);
}
});
// Complete purchase flow
await page.goto('/product/123');
await page.click('[data-add-to-cart]');
await page.goto('/checkout');
await page.fill('#email', '[email protected]');
await page.click('[data-purchase]');
// Verify purchase event
const purchaseRequest = analyticsRequests.find(r =>
r.url().includes('en=purchase')
);
expect(purchaseRequest).toBeDefined();
// Verify parameters
const params = new URLSearchParams(purchaseRequest.url());
expect(params.get('ep.transaction_id')).toBeTruthy();
expect(parseFloat(params.get('ep.value'))).toBeGreaterThan(0);
});
Data Layer Validation
// Validate data layer structure
function validatePurchaseEvent(event) {
const errors = [];
// Required fields
if (!event.transaction_id) errors.push('Missing transaction_id');
if (!event.value) errors.push('Missing value');
if (!event.currency) errors.push('Missing currency');
if (!event.items || !event.items.length) errors.push('Missing items');
// Validate items
event.items?.forEach((item, index) => {
if (!item.item_id) errors.push(`Item ${index}: missing item_id`);
if (!item.item_name) errors.push(`Item ${index}: missing item_name`);
if (typeof item.price !== 'number') errors.push(`Item ${index}: invalid price`);
if (typeof item.quantity !== 'number') errors.push(`Item ${index}: invalid quantity`);
});
return errors;
}
// Use in testing
dataLayer.push = (function(originalPush) {
return function(data) {
if (data.event === 'purchase') {
const errors = validatePurchaseEvent(data);
if (errors.length) {
console.error('Purchase event validation failed:', errors);
}
}
return originalPush.apply(this, arguments);
};
})(dataLayer.push);
Monitoring & Alerts
GA4 Anomaly Detection
GA4 automatically detects:
- Unusual traffic spikes
- Conversion drops
- Engagement changes
Set up custom alerts:
- Admin → Custom Alerts (coming soon)
- Use BigQuery for custom monitoring
BigQuery Monitoring
-- Daily conversion tracking alert
DECLARE expected_min INT64 DEFAULT 50;
WITH daily_conversions AS (
SELECT
DATE(TIMESTAMP_MICROS(event_timestamp)) as date,
COUNT(*) as conversions
FROM `project.analytics_XXXXXX.events_*`
WHERE event_name = 'purchase'
AND _TABLE_SUFFIX = FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
GROUP BY date
)
SELECT
date,
conversions,
CASE
WHEN conversions < expected_min THEN 'ALERT: Low conversions'
ELSE 'OK'
END as status
FROM daily_conversions;
Uptime Monitoring
Monitor critical endpoints:
| Endpoint | Check | Alert Threshold | |----------|-------|-----------------| | GTM container | Response 200 | 5 seconds | | Server-side endpoint | Health check | 2 seconds | | GA4 collection | Sample request | 5 seconds |
Debugging Cheat Sheet
Quick Diagnostics
// 1. Is GTM loaded?
google_tag_manager // Should be defined
// 2. What's in data layer?
console.table(dataLayer.filter(x => typeof x === 'object'))
// 3. Is GA4 active?
window.google_tag_data // Should contain gtag data
// 4. Check consent state
dataLayer.filter(x => x[0] === 'consent')
// 5. Are cookies set?
document.cookie.split(';').filter(c => c.includes('_ga'))
URL Debug Parameters
| Parameter | Purpose |
|-----------|---------|
| ?debug_mode=1 | Enable GA4 DebugView |
| ?gtm_debug=x | Force GTM preview |
| ?_gl=... | Cross-domain linker |
Previous: Attribution Modeling Next: SaaS Analytics