# Failure modes and detection

Each entry gives the symptom, how to detect it read-only, and the fix. GA4 checks use Google's GA4 MCP server (`run_report`, read-only scope) or the BigQuery export; Data API names are given for `run_report`. Confirm dimension and field names against the live Data API schema before running a query.

## Classification

- Paid in Organic Social.
  - Detect: `run_report` with `sessionDefaultChannelGroup`, `sessionManualSource`, `sessionManualMedium`, `sessionManualCampaignId`, filtered to Organic Social. Any row whose campaign ID is in the platform campaign map is paid spend reported as organic.
  - Fix: change the medium to `paid_social` in the platform template; log the cutover.
- Paid Other growth.
  - Detect: Paid Other sessions by `sessionManualSource` and `sessionManualMedium`. Sources not in Google's lists (`li`, `meta`, `msg`, `an`) paired with paid mediums.
  - Fix: use a listed source (`linkedin`, `facebook`), or record Paid Other as expected with a reason.
- Unassigned from mediums with no rule.
  - Detect: Unassigned sessions grouped by manual source and medium; compare with `allowed-pairs.csv`.
  - Fix: map to an allowed pair, or add a custom channel rule (offline).
- Google Ads clicks in Paid Search that should be Cross-network.
  - Detect: Google Ads campaign IDs from the map showing as `google / cpc` manual traffic with Paid Search for Performance Max or Demand Gen campaigns.
  - Fix: expected when manual UTMs are present and the click ID could not be used. Check redirects keep `gclid`, `gbraid`, `wbraid` and `gad_*`; reconsider whether the manual set is needed.

## Values

- Literal or encoded macros.
  - Detect: `sessionManualCampaignId`, `sessionManualCampaignName` and `sessionManualAdContent` values matching `[{}]|%7B|%7D|%257B|CAMPAIGN_ID|CREATIVE_ID|CAMPAIGN_NAME`. In BigQuery, the same regex on `collected_traffic_source.manual_campaign_id`.
  - Fix: correct the macro syntax for that platform; for `%257B`, find the ESP or redirect that encodes twice.
- Case splits.
  - Detect: BigQuery, `collected_traffic_source`: group by `LOWER(manual_source)`, `LOWER(manual_medium)` and keep groups where `COUNT(DISTINCT manual_source) > 1` or `COUNT(DISTINCT manual_medium) > 1`. In GA4 cost import, a low match rate.
  - Fix: lowercase at entry; map historic values in the warehouse.
- Personal data in parameters.
  - Detect: the builder's `@` and digit-run checks on static rows; in BigQuery, `manual_content` or `manual_term` containing `@` or 7 or more digits.
  - Fix: remove the merge tag or field from the link; follow the team's process for purging collected data.
- Uppercase keys (`UTM_SOURCE`) or UTMs after `#`.
  - Detect: test evidence where the final URL has `UTM_` keys or `utm_` after `#`, and the next day shows no campaign for the test.
  - Fix: lowercase keys; the builder puts the fragment last. Treat both as lost unless a test visit shows the property reads them.

## Platforms

- Meta name drift.
  - Detect: BigQuery, `collected_traffic_source.manual_campaign_id` with more than one `manual_campaign_name`, or a name that differs from the map's current `platform_campaign_name`.
  - Fix: expected after a rename (snapshot). Add a dated map row; report by campaign ID.
- Duplicate keys (LinkedIn creative UTMs, LinkedIn account and campaign parameters both set, Microsoft or ESP auto-tagging next to registry values).
  - Detect: the served final URL repeats a `utm_` key; Microsoft's URL test tool output; LinkedIn `GET /rest/adTrackingParameters` returning parameters for both the account and the campaign.
  - Fix: one source of UTMs per link.
- Microsoft import overwrite.
  - Detect: after a scheduled Google Ads import, the Microsoft account or campaign suffix contains Google ValueTrack syntax (`{campaignid}`, `{_campaign}`, `{creative}`), or the URL test tool shows repeated or unresolved keys.
  - Fix: remove URL options from the import; restore the Microsoft suffix.
- Missing Google Ads campaign custom parameter.
  - Detect: GAQL through the Google Ads MCP server (confirm field names with `get_resource_metadata`):

```sql
SELECT campaign.id, campaign.name, campaign.status, campaign.url_custom_parameters
FROM campaign
WHERE campaign.status = 'ENABLED'
```

  Rows with no `campaign` key, or a value different from `campaign.name`, fail. In the CRM, records with `utm_id` set and `utm_campaign` empty or literal.
  - Fix: add or correct the parameter; it must equal the campaign name.
- Meta formats that skip ad URL parameters (boosted posts, lead forms, other formats created outside the ad).
  - Detect: Meta sessions or CRM leads for a live Meta campaign with no `utm_id`; a test of one ad per format.
  - Fix: verify the format's tagging live and tag it where Meta allows; otherwise record the gap.

## Journey

- Stripped parameters.
  - Detect: GA4's missing URL parameter diagnostic (data quality indicator); test evidence where the final URL lacks keys the published URL had; sessions from shortener or link-in-bio domains as Referral with no manual campaign.
  - Fix: fix the redirect, shortener or bio tool so it passes the query string.
- Query string lost before the Google tag reads `page_location` (single-page apps, client-side redirects, consent banners that reload).
  - Detect: test evidence where the address bar shows the UTMs but the first GA4 request's page location does not.
  - Fix: send the first page view before the URL changes, or keep the query on the redirect target. Consent timing goes to consent-debugging.
- Internal tags.
  - Detect: `sessionManualSource` equal to one of your own hostnames or brand slugs; tagged URLs in your own navigation found by a site crawl.
  - Fix: remove the tags; measure internal promotions with events.

## Cost

- Cost with no sessions next to it.
  - Detect: Non Google campaign report rows with cost and zero sessions; the validation report's unmatched rows.
  - Fix: compare the file's source, medium, ID and name with the logged values; rebuild the file from the map.
- Platform campaign with spend and no map row.
  - Detect: platform export campaign IDs with spend in the last 7 days that are not in the map.
  - Fix: add the map row from a read-only export; never guess its values.
