Troubleshooting Tracking Events Not Firing on Drupal | Blue Frog Docs

Troubleshooting Tracking Events Not Firing on Drupal

Comprehensive guide to diagnosing and fixing analytics tracking issues on Drupal sites

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:

  1. Open DevTools (F12)
  2. Go to Network tab
  3. Reload page
  4. Filter by domain:
    • google-analytics.com or googletagmanager.com (GA/GTM)
    • facebook.com or connect.facebook.net (Meta Pixel)

Expected Results:

  • gtag/js?id=G-XXXXXXX or gtm.js?id=GTM-XXXXX
  • collect or /tr requests 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:

  • Script blocked by ad blocker
  • Script load order incorrect
  • Typo in tracking code
  • Module conflict

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 defined error
  • dataLayer is not defined error
  • 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.dataLayer is 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

  1. Open incognito window
  2. Open DevTools
  3. Go to Network tab
  4. Perform action (form submit, purchase, etc.)
  5. Check for tracking requests
  6. Verify parameters

3. Validation

GA4 DebugView:

  1. GA4 → Configure → DebugView
  2. Enable debug mode in browser
  3. Perform actions
  4. View events in real-time

GTM Preview:

  1. GTM → Preview
  2. Enter site URL
  3. Navigate and interact
  4. 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

Resources


Next Steps

// SYS.FOOTER