Events Not Firing on Salesforce Commerce Cloud
General Guide: See Global Events Not Firing Guide for universal concepts and fixes.
SFCC-Specific Causes
1. Page Caching Conflicts
SFCC's aggressive caching can prevent dynamic data layer values:
<!-- Problem: Cached page has stale customer data -->
<iscache type="relative" hour="24"/>
<script>
dataLayer.push({
'userLoggedIn': ${customer.authenticated} // Cached value!
});
</script>
2. AJAX-Based Cart Updates
SFRA cart operations use AJAX, requiring client-side event handling:
// Problem: Cart events handled without tracking
$('.add-to-cart').on('click', function() {
$.post('/Cart-AddProduct', data); // No tracking!
});
3. Controller Response Format
Non-JSON responses don't include tracking data:
// Problem: Render response without tracking data
server.get('AddProduct', function (req, res, next) {
res.render('cart/miniCart', viewData); // No event data
return next();
});
4. Content Security Policy Blocking
Strict CSP can block GTM or analytics scripts:
Content-Security-Policy: script-src 'self'; // Blocks GTM!
5. Consent Management Conflicts
Third-party consent tools blocking tracking:
// OneTrust or similar blocking analytics
if (typeof OptanonActiveGroups === 'undefined') {
// Scripts blocked by consent
}
SFCC-Specific Fixes
Fix 1: Disable Caching for Dynamic Data
Use cache bypass for user-specific data:
<!-- Exclude from page cache -->
<iscache status="off"/>
<!-- Or use remote include for dynamic parts -->
<isinclude url="${URLUtils.url('DataLayer-Get')}" />
Better approach - use client-side API:
// Fetch dynamic data client-side
$(document).ready(function() {
$.get('/DataLayer-GetUserData', function(data) {
dataLayer.push(data);
});
});
Fix 2: Hook into SFRA Events
Use SFRA's built-in event system:
// Listen for SFRA cart events
$(document).on('cart:update', function(e, data) {
if (data.action === 'add') {
gtag('event', 'add_to_cart', {
currency: data.currency,
value: data.totalPrice,
items: data.items
});
}
});
// Product added successfully
$(document).on('product:afterAddToCart', function(e, data) {
gtag('event', 'add_to_cart', {
items: [{
item_id: data.pid,
item_name: data.pname,
price: data.price,
quantity: data.quantity
}]
});
});
Fix 3: Include Tracking Data in Controller Responses
Add event data to JSON responses:
// cartridges/app_custom/cartridge/controllers/Cart.js
server.extend(Cart);
server.append('AddProduct', function (req, res, next) {
var viewData = res.getViewData();
if (!viewData.error) {
var addedProduct = viewData.cart.items[viewData.cart.items.length - 1];
viewData.trackingEvent = {
event: 'add_to_cart',
items: [{
item_id: addedProduct.id,
item_name: addedProduct.productName,
price: addedProduct.price.sales.value,
quantity: addedProduct.quantity
}],
value: addedProduct.price.sales.value * addedProduct.quantity,
currency: viewData.cart.totals.currencyCode
};
}
res.setViewData(viewData);
return next();
});
module.exports = server.exports();
Client-side handler:
// Handle tracking from AJAX response
$(document).ajaxComplete(function(event, xhr, settings) {
if (settings.url.includes('Cart-AddProduct')) {
try {
var response = JSON.parse(xhr.responseText);
if (response.trackingEvent) {
gtag('event', response.trackingEvent.event, {
items: response.trackingEvent.items,
value: response.trackingEvent.value,
currency: response.trackingEvent.currency
});
}
} catch (e) {
console.error('Tracking parse error:', e);
}
}
});
Fix 4: Configure CSP for Analytics
Update CSP in Business Manager or controller:
// In controller or custom CSP cartridge
response.setHttpHeader('Content-Security-Policy',
"script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com; " +
"connect-src 'self' https://www.google-analytics.com https://analytics.google.com https://region1.google-analytics.com; " +
"img-src 'self' https://www.googletagmanager.com https://www.google-analytics.com;"
);
Fix 5: Handle Consent Mode
Implement Google Consent Mode:
// Before GTM loads
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// Default to denied
gtag('consent', 'default', {
'ad_storage': 'denied',
'analytics_storage': 'denied',
'wait_for_update': 500
});
// Update on consent
function updateConsent(granted) {
gtag('consent', 'update', {
'ad_storage': granted ? 'granted' : 'denied',
'analytics_storage': granted ? 'granted' : 'denied'
});
}
// Hook into your consent tool
OneTrust.OnConsentChanged(function() {
var analyticsConsent = OnetrustActiveGroups.includes('C0002');
updateConsent(analyticsConsent);
});
Debugging Checklist
Step 1: Verify Script Loading
// Check GTM loaded
console.log('GTM:', typeof google_tag_manager);
// Check GA loaded
console.log('gtag:', typeof gtag);
// Check dataLayer
console.log('dataLayer:', dataLayer);
Step 2: Check Network Requests
In browser DevTools Network tab:
- Filter by
googleoranalytics - Look for failed requests (red)
- Check request payloads
Step 3: Use GTM Preview
Step 4: Check SFCC Logs
Business Manager > Administration > Operations > Custom Object Editor
Look for JavaScript errors in custom logging.
Common Error Patterns
| Symptom | Likely Cause | Solution |
|---|---|---|
| No events at all | Script blocked or not loaded | Check CSP and network |
| Events on some pages | Inconsistent implementation | Review all controllers |
| Duplicate events | Multiple listeners | Deduplicate event handlers |
| Missing parameters | Data layer not populated | Check ISML rendering |
| Wrong values | Caching issues | Use client-side API calls |