# UTM Cleanup Discrepancy Fix

**Last Updated:** 2026-01-29

## Problem Summary

User reported that UTM parameters were being cleaned from URL on `/gastro` and `/schichtbetriebe` but NOT on `/v3`. Additionally, tracking worked on `/v3` but failed on the other pages.

## Root Cause Analysis

### Issue 1: Cleanup Condition Check Timing

The cleanup function `setupUTMCleanup()` checked for UTM parameters at initialization time, but by the time the cleanup actually executed (1.5 seconds later), the URL might have been modified or parameters might have been removed by other scripts. This could cause cleanup to fail silently.

**Location:** `v2/js/utm-tracking.js` lines 1654-1683

### Issue 2: Silent Failure of history.replaceState()

The `cleanUTMParametersFromURL()` function used `history.replaceState()` which can fail silently due to browser security restrictions. Errors were only logged in debug mode, making failures invisible in production.

**Location:** `v2/js/utm-tracking.js` lines 1702-1750

### Issue 3: Incomplete Error Handling

The cleanup function had a try-catch block, but errors were only logged when debug mode was enabled. This meant cleanup failures went unnoticed in production.

## Solution Implemented

### Fix 1: Re-check URL Parameters at Cleanup Time

Modified `setupUTMCleanup()` to re-check URL parameters when cleanup actually runs, not just at setup time:

```javascript
setTimeout(() => {
    // CRITICAL FIX: Re-check URL parameters at cleanup time, not just setup time
    const currentUrlParams = new URLSearchParams(window.location.search);
    const stillHasUTMParams = this.utmParams.some(param => currentUrlParams.has(param));
    const stillHasTrackingParams = ['gclid', 'leadSource', 'lead_source', 'partner', 'signuptype'].some(param => currentUrlParams.has(param));

    if (stillHasUTMParams || stillHasTrackingParams) {
        this.cleanUTMParametersFromURL();
        // Dispatch cleanup event...
    } else {
        // Log if cleanup was skipped
        if (this.debugMode) {
            this.logDebug('Cleanup skipped - UTM parameters no longer in URL', {...});
        }
    }
}, 1500);
```

**Benefits:**

- Ensures cleanup only runs if parameters still exist
- Prevents unnecessary cleanup attempts
- Provides better debugging information

### Fix 2: Enhanced Error Handling for history.replaceState()

Added explicit try-catch around `history.replaceState()` with detailed error logging:

```javascript
try {
  window.history.replaceState({}, document.title, url.toString());
  // Success logging...
} catch (replaceStateError) {
  // Log error with details
  if (this.debugMode) {
    this.logDebug("Failed to update URL via replaceState", {
      error: replaceStateError.message,
      errorName: replaceStateError.name,
      originalUrl: originalUrl,
      attemptedUrl: url.toString(),
      removedParams: removedParams,
    });
  }
  throw replaceStateError; // Re-throw for outer catch
}
```

**Benefits:**

- Catches `SecurityError` exceptions from browser restrictions
- Provides detailed error information in debug mode
- Helps identify browser-specific issues

### Fix 3: Improved Cleanup Function Logging

Enhanced `cleanUTMParametersFromURL()` with:

- Better logging of removed parameters
- Detection of "no cleanup needed" scenarios
- More detailed error information

```javascript
const removedParams = [];
// Track which parameters were removed
this.utmParams.forEach((param) => {
  if (url.searchParams.has(param)) {
    removedParams.push(param);
    url.searchParams.delete(param);
    hasUTMParams = true;
  }
});

if (hasUTMParams) {
  // Attempt cleanup...
} else {
  if (this.debugMode) {
    this.logDebug("No UTM parameters to clean - URL already clean", {
      currentUrl: originalUrl,
    });
  }
}
```

**Benefits:**

- Clear visibility into what was cleaned
- Identifies when cleanup isn't needed
- Better debugging information

## Testing

### Test Script Created

Created comprehensive test script: `v2/scripts/dev-helpers/test-cleanup-behavior.php`

**Usage:**

```
http://localhost:8003/v2/scripts/dev-helpers/test-cleanup-behavior.php?page=v3&utm_source=test&utm_campaign=test&utm_debug=true
```

**Features:**

- Tests script loading
- Verifies tracker initialization
- Checks cookie setting
- Monitors cleanup execution
- Verifies final URL state
- Real-time console logging

### Expected Behavior After Fix

1. **All Pages:** Cleanup should run consistently if UTMs are present
2. **Cookie Setting:** Cookies are set before cleanup runs (in `init()` order)
3. **Form Reading:** Forms read from `window.utmTracker.getUTMDataForAPI()` which uses:
   - Instance variables (persist after cleanup)
   - Cookies (fallback)
   - localStorage (fallback)
   - URL parameters (last resort)

## Why Tracking Worked on /v3 Before Fix

On `/v3`, cleanup was failing (possibly due to timing or `history.replaceState()` errors), which meant:

- UTMs stayed in URL
- Forms read from URL (fallback in `populateUTMFields()`)
- Tracking worked by accident

On `/gastro` and `/schichtbetriebe`:

- Cleanup ran successfully
- UTMs were removed from URL
- Forms should read from cookies/instance vars
- If cookies weren't set properly, tracking failed

## Verification Checklist

After deploying fixes, verify:

1. **Cleanup Consistency:**
   - Visit `/v3?utm_source=test&utm_campaign=test&utm_debug=true`
   - Visit `/gastro?utm_source=test&utm_campaign=test&utm_debug=true`
   - Visit `/schichtbetriebe?utm_source=test&utm_campaign=test&utm_debug=true`
   - Check console logs - cleanup should run on all pages
   - Verify UTMs are removed from URL after 1.5 seconds

2. **Cookie Setting:**
   - Check cookies: `document.cookie.match(/utm_/g)`
   - Verify cookies are set before cleanup runs
   - Check cookie expiration (90 days)

3. **Form Tracking:**
   - Submit forms on all pages
   - Verify UTM data is sent to HubSpot
   - Check Network tab for form submissions
   - Verify UTM parameters in form payload

4. **Debug Mode:**
   - Add `?utm_debug=true` to URLs
   - Check console for detailed cleanup logs
   - Verify error messages if cleanup fails

## Files Modified

1. `v2/js/utm-tracking.js`
   - `setupUTMCleanup()` - Added re-check of URL parameters at cleanup time
   - `cleanUTMParametersFromURL()` - Enhanced error handling and logging

2. `v2/scripts/dev-helpers/test-cleanup-behavior.php` (NEW)
   - Comprehensive test script for cleanup behavior

## Related Documentation

- `docs/development/UTM_TRACKING_DEBUGGING.md` - General UTM debugging guide
- `docs/development/UTM_TRACKING_VERIFICATION_CHECKLIST.md` - Verification steps
- `docs/google-ads/UTM_PARAMETER_FIX_SCHICHTBETRIEBE.md` - Google Ads configuration

## Next Steps

1. Deploy fixes to production
2. Monitor cleanup behavior across all pages
3. Verify form submissions include UTM data
4. Check HubSpot for proper attribution
5. Update monitoring/alerts if needed
