This guide helps you diagnose and fix common issues when analytics events aren't firing on Kentico Xperience websites.
Quick Diagnostic Checklist
Before diving deep, run through this quick checklist:
- Tracking code visible in page source
- Correct tracking ID/pixel ID/container ID
- No JavaScript errors in console
- Not blocked by ad blocker
- Correct environment (not staging)
- User has given consent (if using consent management)
- Browser cache cleared
- Testing in incognito/private mode
Browser Console Diagnostics
Check for JavaScript Errors
- Open DevTools (F12)
- Go to Console tab
- Look for red errors
- Common errors:
gtag is not definedfbq is not defineddataLayer is not defined
Verify Tracking Functions Loaded
// In browser console, type:
// Check if GA4 is loaded
typeof gtag
// Should return "function"
// Check if Meta Pixel is loaded
typeof fbq
// Should return "function"
// Check if GTM dataLayer exists
typeof dataLayer
// Should return "object"
// Check GTM container
typeof google_tag_manager
// Should return "object"
Test Events Manually
// Test GA4 event
gtag('event', 'test_event', { test_param: 'test_value' });
// Test Meta Pixel event
fbq('track', 'PageView');
fbq('trackCustom', 'TestEvent', { test: 'value' });
// Test GTM dataLayer
dataLayer.push({ 'event': 'test_event', 'test_param': 'test_value' });
Google Analytics 4 Troubleshooting
Issue: GA4 Not Loading
Check if gtag.js is Present
View page source (Ctrl+U) and search for:
https://www.googletagmanager.com/gtag/js?id=G-
If not found:
Solution 1: Verify Layout File
@using CMS.Helpers
@{
var gaId = SettingsKeyInfoProvider.GetValue("GoogleAnalyticsMeasurementID", SiteContext.CurrentSiteID);
}
@if (!string.IsNullOrEmpty(gaId))
{
<script async src="https://www.googletagmanager.com/gtag/js?id=@gaId"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '@gaId');
</script>
}
else
{
<!-- DEBUG: GA4 ID is empty -->
}
Solution 2: Check Kentico Settings
- Go to Settings → Integration → Google
- Verify
GoogleAnalyticsMeasurementIDexists - Check value format:
G-XXXXXXXXXX(notUA-orGTM-) - Ensure setting is at correct site level (multi-site)
Check for Environment Blocking
@{
var isProduction = SiteContext.CurrentSite.SiteName == "ProductionSite";
var gaId = SettingsKeyInfoProvider.GetValue("GoogleAnalyticsMeasurementID", SiteContext.CurrentSiteID);
// DEBUG: Add this temporarily
var debugInfo = $"Site: {SiteContext.CurrentSite.SiteName}, IsProduction: {isProduction}, GA ID: {gaId}";
}
<!-- Debug info (remove in production): @debugInfo -->
@if (isProduction && !string.IsNullOrEmpty(gaId))
{
<!-- GA4 code -->
}
Issue: GA4 Events Not Firing
Enable Debug Mode
<script>
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
</script>
Then check GA4 → Configure → DebugView
Verify Event Syntax
// INCORRECT
gtag('event', 'purchase' {
value: 100 // Missing comma before object
});
// CORRECT
gtag('event', 'purchase', {
value: 100,
currency: 'USD'
});
Check Event Timing
// BAD: Event fires before gtag is loaded
gtag('event', 'page_view');
// GOOD: Wait for gtag to load
window.addEventListener('load', function() {
gtag('event', 'page_view');
});
Verify in Network Tab
- Open DevTools → Network tab
- Filter by "collect"
- Trigger your event
- Look for requests to
google-analytics.com/g/collect - Click request → Payload tab
- Verify event name and parameters
Issue: E-commerce Events Not Tracking
Check Purchase Event Implementation
@using CMS.Ecommerce
@model OrderInfo
@{
var purchaseTracked = SessionHelper.GetValue("GA4_Purchase_" + Model.OrderID);
}
<!-- Debug: Purchase Tracked = @purchaseTracked -->
@if (purchaseTracked == null)
{
SessionHelper.SetValue("GA4_Purchase_" + Model.OrderID, true);
<script>
console.log('Tracking purchase:', {
transaction_id: '@Model.OrderID',
value: @Model.OrderGrandTotal,
currency: '@Model.OrderCurrency.CurrencyCode'
});
gtag('event', 'purchase', {
transaction_id: '@Model.OrderID',
value: @Model.OrderGrandTotal,
currency: '@Model.OrderCurrency.CurrencyCode',
items: [
@foreach (var item in Model.OrderItems)
{
@:{
item_id: '@item.OrderItemSKU.SKUNumber',
item_name: '@item.OrderItemSKU.SKUName',
price: @item.OrderItemUnitPrice,
quantity: @item.OrderItemUnitCount
}@(item != Model.OrderItems.Last() ? "," : "")
}
]
});
</script>
}
Common E-commerce Issues
Problem: Currency not a 3-letter code
// BAD
currency: 'Dollar'
// GOOD
currency: 'USD'
Problem: Value is string instead of number
// BAD
value: '@Model.OrderGrandTotal' // String with quotes
// GOOD
value: @Model.OrderGrandTotal // Number without quotes
Google Tag Manager Troubleshooting
Issue: GTM Not Loading
Verify Container Code
View page source and search for: googletagmanager.com/gtm.js?id=GTM-
If not found, check layout file:
<!DOCTYPE html>
<html>
<head>
<!-- THIS MUST BE IN HEAD -->
@{
var gtmId = SettingsKeyInfoProvider.GetValue("GoogleTagManagerID", SiteContext.CurrentSiteID);
}
@if (!string.IsNullOrEmpty(gtmId))
{
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','@gtmId');</script>
<!-- End Google Tag Manager -->
}
else
{
<!-- DEBUG: GTM ID is empty! -->
}
</head>
<body>
<!-- THIS MUST BE AT START OF BODY -->
@if (!string.IsNullOrEmpty(gtmId))
{
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=@gtmId"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
}
@RenderBody()
</body>
</html>
Check Container Published
- Open GTM
- Verify container has been published (not just saved)
- Check Versions tab for latest version
- Publish if needed
Issue: GTM Tags Not Firing
Use Preview Mode
- Open GTM
- Click Preview
- Enter your Kentico site URL
- GTM debugger opens
In debugger:
- Summary tab shows tags fired/not fired
- Variables tab shows variable values
- Data Layer tab shows data layer events
- Errors tab shows problems
Check Triggers
Common trigger issues:
Issue: Trigger condition not met
// Trigger: Click - All Elements
// Condition: Click URL contains "download"
// This WON'T fire because link uses JavaScript onclick
<a href="#"
// This WILL fire
<a href="/downloads/file.pdf">Download</a>
Solution: Adjust trigger conditions or add data attributes
<a href="#"
data-download="true">Download</a>
// Trigger: Click - All Elements
// Condition: Click Element matches CSS selector a[data-download]
Check Variables
Issue: Variable returns undefined
// In GTM, check variable configuration
// Variable Type: Data Layer Variable
// Data Layer Variable Name: ecommerce.value
// In your page, verify structure:
dataLayer.push({
'ecommerce': {
'value': 100 // Variable will find this
}
});
Debug variables in console:
// List all dataLayer events
console.table(dataLayer);
// Check specific variable
dataLayer.filter(item => item.event === 'purchase');
Issue: Data Layer Not Populating
Check Initialization
<!-- Data Layer MUST be defined BEFORE GTM container -->
<!-- CORRECT ORDER -->
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'pageType': 'product',
'productId': '12345'
});
</script>
<!-- GTM Container code here -->
<!-- WRONG: GTM loads first -->
<!-- GTM Container -->
<script>
window.dataLayer.push({ ... }); // Too late!
</script>
Verify Kentico Data Layer Structure
@using CMS.DocumentEngine
@using Newtonsoft.Json
@{
var currentDoc = DocumentContext.CurrentDocument;
var pageData = new {
pageType = currentDoc.ClassName,
pageName = currentDoc.DocumentName,
pageId = currentDoc.DocumentID
};
}
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push(@Html.Raw(JsonConvert.SerializeObject(new {
pageData = pageData
})));
// Debug: Log to console
console.log('Data Layer:', window.dataLayer);
</script>
Meta Pixel Troubleshooting
Issue: Pixel Not Loading
Check Pixel Code Present
View source and search for: connect.facebook.net/en_US/fbevents.js
If not found:
@{
var pixelId = SettingsKeyInfoProvider.GetValue("FacebookPixelID", SiteContext.CurrentSiteID);
}
<!-- Debug: Pixel ID = @pixelId -->
@if (!string.IsNullOrEmpty(pixelId))
{
<script>
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
console.log('Initializing Meta Pixel:', '@pixelId');
fbq('init', '@pixelId');
fbq('track', 'PageView');
</script>
}
Use Meta Pixel Helper
- Install Meta Pixel Helper Chrome extension
- Navigate to your site
- Click extension icon
- Check for:
- ✓ Pixel found
- Pixel ID matches yours
- Events firing
- No errors
Issue: Pixel Events Not Firing
Check Event Order
// BAD: Event fires before pixel loaded
fbq('track', 'AddToCart'); // Error: fbq not defined
// GOOD: Ensure pixel loads first
if (typeof fbq !== 'undefined') {
fbq('track', 'AddToCart', {
content_ids: ['12345'],
content_type: 'product'
});
}
Verify Event Parameters
// Check in browser console
window._fbq.instance.pendingConfigs
// Shows all pixel configurations
// Check events queue
window._fbq.instance.queue
// Shows queued events
Common Event Issues
Issue: Event name incorrect (case-sensitive)
// WRONG
fbq('track', 'addToCart'); // Lowercase
// CORRECT
fbq('track', 'AddToCart'); // Proper case
Issue: Missing content_ids for e-commerce events
// INCOMPLETE
fbq('track', 'Purchase', {
value: 100,
currency: 'USD'
// Missing content_ids!
});
// COMPLETE
fbq('track', 'Purchase', {
value: 100,
currency: 'USD',
content_ids: ['SKU123', 'SKU456']
});
Issue: Events in Test Events But Not Production
Check Pixel Deduplication
If using both browser pixel and Conversions API:
fbq('track', 'Purchase', {
value: 100,
currency: 'USD'
}, {
eventID: 'purchase_@Model.OrderID' // Unique event ID
});
Server-side must use same eventID.
Kentico-Specific Issues
Issue: Tracking Works on Some Page Types, Not Others
Check Page Template
Different templates may have different layouts:
@{
// Add debug to identify which layout is loading
var layoutUsed = "Main Layout";
}
<!-- DEBUG: Layout = @layoutUsed -->
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Check Portal Engine Master Pages
In Portal Engine, different page types might use different master pages:
- Go to Design → Page templates
- Check each template's master page
- Ensure all have tracking code
Solution: Centralize Tracking Code
Create a shared partial view:
<!-- Views/Shared/_Tracking.cshtml -->
@using CMS.Helpers
@{
var gtmId = SettingsKeyInfoProvider.GetValue("GoogleTagManagerID", SiteContext.CurrentSiteID);
var gaId = SettingsKeyInfoProvider.GetValue("GoogleAnalyticsMeasurementID", SiteContext.CurrentSiteID);
}
@if (!string.IsNullOrEmpty(gtmId))
{
<!-- GTM code -->
}
@if (!string.IsNullOrEmpty(gaId))
{
<!-- GA4 code -->
}
Then include in all layouts:
@Html.Partial("_Tracking")
Issue: Events Fire in Development, Not Production
Check Environment Conditionals
@{
var siteName = SiteContext.CurrentSite.SiteName;
var isProduction = siteName == "ProductionSite"; // Check this matches!
}
<!-- Debug: Site Name = @siteName, Is Production = @isProduction -->
@if (isProduction)
{
<!-- Tracking code -->
}
Check Setting Inheritance
Settings might be defined at global level but overridden at site level:
- Go to Settings → [Your Site Name]
- Check if setting exists at site level
- Verify value is correct for that site
Issue: ViewState Interfering (Portal Engine)
Large ViewState can cause timing issues:
<!-- web.config -->
<appSettings>
<!-- Disable ViewState if not needed -->
<add key="CMSControlState" value="false" />
</appSettings>
<!-- Or per-page -->
<%@ Page EnableViewState="false" %>
Consent Management Issues
Issue: Tracking Blocked by Consent
Check Consent Status
// In console, check consent status
// For OneTrust
console.log(OptanonActiveGroups);
// For Cookiebot
console.log(Cookiebot.consent);
// For custom implementation
console.log(localStorage.getItem('analyticsConsent'));
Conditional Loading Based on Consent
<script>
function loadTracking() {
if (hasUserConsent()) {
// Load GTM
(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXXXX');
}
}
function hasUserConsent() {
// Your consent check logic
return localStorage.getItem('analyticsConsent') === 'true';
}
// Load on consent granted
window.addEventListener('consentGranted', loadTracking);
// Check on page load
if (hasUserConsent()) {
loadTracking();
}
</script>
Network-Level Issues
Issue: Content Security Policy (CSP) Blocking
Check browser console for CSP errors:
Refused to load the script 'https://www.googletagmanager.com/gtm.js'
because it violates the following Content Security Policy directive
Solution: Update CSP headers
<!-- web.config -->
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Content-Security-Policy"
value="script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com https://connect.facebook.net;" />
</customHeaders>
</httpProtocol>
</system.webServer>
Issue: Ad Blockers
Ad blockers can prevent tracking:
For testing:
- Disable ad blocker
- Use incognito mode
- Test on mobile device
For detection:
<script>
// Detect ad blocker
var testAd = document.createElement('div');
testAd.innerHTML = ' ';
testAd.className = 'adsbox';
document.body.appendChild(testAd);
window.setTimeout(function() {
if (testAd.offsetHeight === 0) {
console.warn('Ad blocker detected - tracking may be blocked');
}
testAd.remove();
}, 100);
</script>
Debugging Tools
Enable All Debugging
<script>
// Log all GTM events
window.dataLayer = window.dataLayer || [];
var originalPush = window.dataLayer.push;
window.dataLayer.push = function() {
console.log('GTM Event:', arguments);
return originalPush.apply(window.dataLayer, arguments);
};
// Log all GA4 events
window.gtag = window.gtag || function() {
console.log('GA4 Event:', arguments);
window.dataLayer.push(arguments);
};
// Log all Meta Pixel events
window.fbq = window.fbq || function() {
console.log('Meta Pixel Event:', arguments);
};
</script>
Create Debug Panel
@if (User.IsInRole("Administrator"))
{
<div id="debug-panel" style="position: fixed; bottom: 0; right: 0; background: #000; color: #0f0; padding: 10px; font-family: monospace; font-size: 12px; max-height: 300px; overflow-y: auto; z-index: 9999;">
<h4>Debug Info</h4>
<p>Site: @SiteContext.CurrentSite.SiteName</p>
<p>GTM ID: @SettingsKeyInfoProvider.GetValue("GoogleTagManagerID", SiteContext.CurrentSiteID)</p>
<p>GA4 ID: @SettingsKeyInfoProvider.GetValue("GoogleAnalyticsMeasurementID", SiteContext.CurrentSiteID)</p>
<p>Pixel ID: @SettingsKeyInfoProvider.GetValue("FacebookPixelID", SiteContext.CurrentSiteID)</p>
<p>Page Type: @DocumentContext.CurrentDocument?.ClassName</p>
<button window.dataLayer)">Log DataLayer</button>
<button google_tag_manager)">Log GTM</button>
</div>
}
Testing Checklist
Before declaring victory, test:
- Page view tracking works
- Custom events fire correctly
- E-commerce events track properly
- Events appear in analytics platform
- Works across all page templates
- Works on mobile devices
- Works in all major browsers
- Consent management doesn't break tracking
- No duplicate events
- Production environment tracking correctly
Getting Help
If you're still stuck:
- Check Kentico Event Log: System → Event log
- Review Browser Console: Any JavaScript errors?
- Use Debugging Tools: GTM Preview, GA4 DebugView, Meta Pixel Helper
- Test in Isolation: Create a simple test page
- Compare Working vs Non-Working: What's different?
- Ask Community: Kentico DevNet, analytics forums