Troubleshooting Tracking Events Not Firing on Drupal
Overview
Analytics events may fail to fire on Drupal sites due to caching, JavaScript errors, module conflicts, or BigPipe issues. This guide provides systematic troubleshooting steps for Google Analytics, GTM, Meta Pixel, and custom tracking implementations.
Common Symptoms
- ✗ PageView events not appearing in analytics
- ✗ Custom events not triggering
- ✗ E-commerce transactions not tracked
- ✗ Form submissions not recording
- ✗ Data layer empty or incomplete
- ✗ Duplicate events firing
Diagnostic Checklist
Step 1: Verify Script Loading
Check in Browser DevTools:
- Open DevTools (F12)
- Go to Network tab
- Reload page
- Filter by domain:
google-analytics.comorgoogletagmanager.com(GA/GTM)facebook.comorconnect.facebook.net(Meta Pixel)
Expected Results:
- ✅
gtag/js?id=G-XXXXXXXorgtm.js?id=GTM-XXXXX - ✅
collector/trrequests with event data - ✅ Status 200 (successful)
If scripts don't load:
- Check module is enabled
- Verify tracking ID is correct
- Clear Drupal cache
- Check for JavaScript errors
Step 2: Check Browser Console
Look for JavaScript errors:
Uncaught ReferenceError: gtag is not defined
Uncaught ReferenceError: fbq is not defined
Uncaught TypeError: Cannot read property 'push' of undefined
Common causes:
Issue 1: Cache-Related Problems
Symptoms
- Events work in incognito but not in normal browser
- Changes to tracking code don't appear
- Old tracking ID still being used
Solutions
1. Clear all Drupal caches:
drush cr
# Or
drush cache:rebuild
2. Clear specific cache bins:
drush cache:clear css-js
drush cache:clear render
drush cache:clear page
3. Check cache contexts:
Ensure proper cache contexts are set:
<?php
function mytheme_page_attachments(array &$attachments) {
// Add tracking library
$attachments['#attached']['library'][] = 'mytheme/analytics';
// Critical: Set cache contexts
$attachments['#cache']['contexts'][] = 'user.roles';
$attachments['#cache']['contexts'][] = 'url.path';
// For user-specific tracking
$attachments['#cache']['contexts'][] = 'session';
// Force no cache for testing (remove in production)
// $attachments['#cache']['max-age'] = 0;
}
4. Bypass page cache for testing:
// settings.local.php (development only)
$config['system.performance']['cache']['page']['max_age'] = 0;
5. Clear browser cache:
- Chrome: Ctrl+Shift+Del → Clear browsing data
- Or use incognito mode
Issue 2: BigPipe Conflicts
Symptoms
- Events fire inconsistently
- Data layer is empty initially
- Scripts load after page render
- Events work without BigPipe
Solutions
1. Load tracking scripts before BigPipe:
<?php
function mytheme_page_attachments(array &$attachments) {
// Use html_head for critical tracking
$attachments['#attached']['html_head'][] = [
[
'#type' => 'html_tag',
'#tag' => 'script',
'#attributes' => [
'src' => 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX',
'async' => TRUE,
],
'#weight' => -1000, // Load very early
],
'google_analytics_script'
];
// Inline initialization (critical)
$attachments['#attached']['html_head'][] = [
[
'#type' => 'html_tag',
'#tag' => 'script',
'#value' => "window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-XXXXXXXXXX');",
'#weight' => -999,
],
'google_analytics_init'
];
}
2. Use BigPipe-compatible placeholders:
<?php
function mytheme_page_attachments(array &$attachments) {
// Create lazy builder for tracking
$attachments['tracking'] = [
'#lazy_builder' => ['mytheme.tracking_builder:build', []],
'#create_placeholder' => FALSE, // Don't use placeholder for tracking
];
}
3. Disable BigPipe on specific pages:
<?php
function mytheme_page_attachments_alter(array &$attachments) {
$route_match = \Drupal::routeMatch();
// Disable on checkout pages where tracking is critical
if ($route_match->getRouteName() === 'commerce_checkout.form') {
foreach ($attachments['#attached']['library'] as $key => $library) {
if ($library === 'big_pipe/big_pipe') {
unset($attachments['#attached']['library'][$key]);
}
}
}
}
Issue 3: JavaScript Execution Order
Symptoms
gtag is not definederrordataLayer is not definederror- Events fire before pixel loads
Solutions
1. Ensure proper load order:
# mytheme.libraries.yml
analytics:
js:
# External script loads first
https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX:
type: external
attributes:
async: true
weight: -10
# Your tracking code loads second
js/analytics.js:
weight: -5
dependencies:
- core/drupal
- core/drupalSettings
2. Wait for scripts to load:
// js/analytics.js
(function (Drupal) {
'use strict';
// Wait for gtag to be available
function waitForGtag(callback) {
if (typeof gtag !== 'undefined') {
callback();
} else {
setTimeout(function() {
waitForGtag(callback);
}, 100);
}
}
Drupal.behaviors.analytics = {
attach: function (context, settings) {
waitForGtag(function() {
// Now safe to use gtag
gtag('event', 'page_view', {
page_title: document.title,
page_location: window.location.href
});
});
}
};
})(Drupal);
3. Use script callbacks:
// Load GTM with callback
(function() {
var gtmScript = document.createElement('script');
gtmScript.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXX';
gtmScript.async = true;
gtmScript.onload = function() {
console.log('GTM loaded');
// Safe to push events now
dataLayer.push({'event': 'gtm_loaded'});
};
document.head.appendChild(gtmScript);
})();
Issue 4: Module Conflicts
Symptoms
- Tracking worked, then stopped
- Multiple tracking codes present
- Different behavior with modules disabled
Solutions
1. Check for multiple tracking implementations:
# Search for tracking IDs in code
grep -r "G-XXXXXXXXXX" themes/ modules/
# Search for GTM containers
grep -r "GTM-" themes/ modules/
# Check enabled modules
drush pm:list --status=enabled | grep -i "analytics\|google\|tag\|pixel"
2. Identify conflicting modules:
# Disable modules one by one
drush pmu module_name -y
drush cr
# Test tracking
# Re-enable
drush en module_name -y
3. Check hook implementation order:
<?php
/**
* Implements hook_module_implements_alter().
*/
function mytheme_module_implements_alter(&$implementations, $hook) {
if ($hook === 'page_attachments') {
// Ensure our implementation runs last
$group = $implementations['mytheme'];
unset($implementations['mytheme']);
$implementations['mytheme'] = $group;
}
}
4. View all page attachments:
<?php
// Temporary debug code
function mytheme_page_attachments(array &$attachments) {
// Log all libraries being attached
if (!empty($attachments['#attached']['library'])) {
\Drupal::logger('mytheme')->debug('Libraries: @libs', [
'@libs' => print_r($attachments['#attached']['library'], TRUE)
]);
}
}
Issue 5: Data Layer Not Populating
Symptoms
window.dataLayeris undefined or empty- GTM tags not firing
- Data layer variables missing
Solutions
1. Initialize data layer before GTM:
// Must run before GTM script
window.dataLayer = window.dataLayer || [];
2. Check data layer module configuration:
drush config:get datalayer.settings
3. Verify data layer output:
<?php
function mytheme_page_attachments(array &$attachments) {
// Add data layer data
$data_layer = [
'pageType' => 'article',
'userId' => \Drupal::currentUser()->id(),
];
$attachments['#attached']['html_head'][] = [
[
'#type' => 'html_tag',
'#tag' => 'script',
'#value' => 'window.dataLayer = window.dataLayer || []; dataLayer.push(' . json_encode($data_layer) . ');',
'#weight' => -1000,
],
'datalayer_init'
];
}
4. Debug data layer in console:
// View entire data layer
console.log(window.dataLayer);
// Monitor all pushes
var originalPush = dataLayer.push;
dataLayer.push = function() {
console.log('DataLayer Push:', arguments[0]);
return originalPush.apply(dataLayer, arguments);
};
Issue 6: Events Fired But Not Recorded
Symptoms
- Network requests show events being sent
- No errors in console
- Events don't appear in analytics
Solutions
1. Check tracking ID:
// Verify correct tracking ID
console.log(gtag.getAll()); // For GA4
// Or check dataLayer
console.log(dataLayer.filter(function(item) {
return item.event === 'gtm.js';
}));
2. Verify event parameters:
// GA4 events must follow naming conventions
gtag('event', 'purchase', { // ✅ Lowercase, underscores
transaction_id: '12345',
value: 99.99,
currency: 'USD'
});
// Not:
gtag('event', 'Purchase', { // ✗ Uppercase
transactionId: '12345', // ✗ camelCase
price: 99.99 // ✗ Wrong parameter name
});
3. Check filters in analytics:
- GA4: Check data filters
- GTM: Check tag filters and triggers
- Meta: Check event deduplication
4. Wait for processing:
- GA4: Can take 24-48 hours for events to appear
- Use DebugView for real-time validation
Issue 7: AJAX Form Submission Events
Symptoms
- Standard forms tracked correctly
- AJAX forms not tracked
- Events fire on page load but not AJAX
Solutions
1. Use Drupal AJAX events:
(function (Drupal, once) {
'use strict';
Drupal.behaviors.ajaxFormTracking = {
attach: function (context, settings) {
// Listen for AJAX form success
once('ajax-form-tracking', 'body', context).forEach(function() {
$(document).on('ajaxSuccess', function(event, xhr, ajaxSettings) {
// Check if this is a form submission
if (ajaxSettings.url.indexOf('/webform/') !== -1) {
gtag('event', 'form_submit', {
event_category: 'webform',
event_label: 'ajax_form',
transport_type: 'beacon'
});
}
});
});
}
};
})(Drupal, once);
2. Track before AJAX submission:
Drupal.behaviors.webformAjaxTracking = {
attach: function (context, settings) {
once('webform-tracking', 'form.webform-submission-form', context).forEach(function(form) {
form.addEventListener('submit', function(event) {
// Track immediately
gtag('event', 'form_submit', {
event_category: 'webform',
event_label: form.id,
transport_type: 'beacon' // Ensures event sends even if page unloads
});
});
});
}
};
Issue 8: Commerce Events Not Firing
Symptoms
- Product views tracked, but not purchases
- Add to cart events missing
- E-commerce data incomplete
Solutions
1. Check event subscriber priority:
<?php
class CommerceTrackingSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
// Use negative priority to run after other subscribers
CartEvents::CART_ENTITY_ADD => ['onCartAdd', -100],
];
}
}
2. Verify session storage:
<?php
// Ensure session is started
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$_SESSION['tracking_events'][] = [
'event' => 'purchase',
'data' => $order_data
];
3. Debug order state transitions:
drush watchdog:show --filter="commerce"
Debugging Tools
Browser Extensions
Google Tag Assistant Legacy:
- Shows all Google tags on page
- Identifies errors and warnings
- Validates implementation
Meta Pixel Helper:
- Shows Meta Pixel events
- Identifies errors
- Shows event parameters
DataSlayer:
- Inspects data layer
- Shows all events
- Validates GTM implementation
Enable Debug Mode
GA4:
gtag('config', 'G-XXXXXXXXXX', {
'debug_mode': true
});
GTM: Use Preview mode: GTM → Preview
Meta Pixel:
fbq('init', 'XXXXXXXXXX', {}, {
debug: true
});
Drupal Debug Configuration
# Enable Devel module
composer require --dev drupal/devel
drush en devel devel_generate webprofiler -y
Check JavaScript settings:
// Add to page
dpm(\Drupal::request()->attributes->get('_route'));
dpm($attachments['#attached']);
Testing Workflow
1. Local Testing
# Clear cache
drush cr
# Check for errors
drush watchdog:show --severity=Error
# View recent logs
drush watchdog:show --count=20
2. Browser Testing
- Open incognito window
- Open DevTools
- Go to Network tab
- Perform action (form submit, purchase, etc.)
- Check for tracking requests
- Verify parameters
3. Validation
GA4 DebugView:
- GA4 → Configure → DebugView
- Enable debug mode in browser
- Perform actions
- View events in real-time
GTM Preview:
- GTM → Preview
- Enter site URL
- Navigate and interact
- View tags, triggers, variables
Common Fixes Summary
# Quick diagnostic checklist
✓ Clear Drupal cache: drush cr
✓ Check browser console for errors
✓ Verify scripts load (Network tab)
✓ Test in incognito mode
✓ Disable ad blocker
✓ Check tracking ID is correct
✓ Verify module is enabled
✓ Check hook implementation order
✓ Test data layer initialization
✓ Review cache contexts
Production Deployment Checklist
Before deploying tracking changes:
- Test in local/dev environment
- Clear all caches
- Test in staging environment
- Verify with browser dev tools
- Check GA4/GTM debugger
- Test all event types
- Verify e-commerce tracking
- Check mobile responsiveness
- Monitor for 24 hours post-deploy
- Review analytics reports
Getting Help
Collect Debug Information
# Drupal info
drush core:status
drush pm:list --status=enabled | grep -i "analytics\|google\|tag"
# Check logs
drush watchdog:show --count=50
# Configuration export
drush config:export
Community Resources
- Drupal Slack: #analytics channel
- Drupal.org forums
- Stack Exchange: drupal.stackexchange.com