The most reliable way to find broken GA4 events and missing properties is to trace one expected event through the collection path: confirm the business action, inspect the dataLayer, check the tag or gtag() call, inspect the browser request, and verify the result in GA4 DebugView and in reports. The sequence tells you whether the failure happened before GA4 received anything, in the payload, or after collection, in reporting. Use DebugView for a focused test, then add production monitoring if the issue could affect visitors you didn’t reproduce yourself.
The key distinction is between collection and reporting. An event can be missing because it never fired. It can also arrive with the wrong name or an incomplete payload. And teams often say “property” for an event parameter, while GA4 user properties are a separate, user-level concept. A custom event parameter can be collected correctly and still be missing from standard reports until you register it as a custom dimension or metric.
Start with the layer where the evidence disappears
Don’t start by changing the GA4 report. First find the last layer where the expected event or property is still present.
| What you observe | What it usually means | Evidence to collect | First place to fix |
|---|---|---|---|
The user completes the action, but the event is not in the dataLayer or the gtag() call | The application logic, form callback, state change, or business trigger didn’t produce the event | The user action, console output, dataLayer state, and release | Site or app implementation |
The event is in the dataLayer, but the GA4 tag doesn’t fire | The GTM trigger, event name, variable, consent condition, or tag sequencing doesn’t match the data | GTM Preview timeline, trigger conditions, variables, and consent state | GTM container |
| The tag fires, but there is no GA4 request | Tag configuration, Measurement ID, consent behavior, the browser, or the transport is blocking collection | GTM tag result and the browser’s Network panel | Tag configuration, consent setup, or browser path |
| The request exists, but a property is missing or wrong | The mapping, variable, object shape, field name, or type is wrong | The request payload and the source dataLayer object | Data mapping or implementation |
| DebugView shows the event and property, but a report doesn’t | The parameter isn’t registered for reporting, or the report has processing or scope limits | DebugView event details, custom definitions, report scope, and processing time | GA4 reporting configuration |
| The event appears twice | Two triggers, two tags, automatic collection, retries, or client and server paths measure one action twice | GTM timeline, request timestamps, event identifiers, and implementation paths | Trigger or deduplication logic |
| The event works on the test device but disappears for some visitors | Consent, ad blockers, browser restrictions, devices, geography, or an untested route change the result | Requests grouped by consent state, browser, device, route, and release | Coverage, consent, or production implementation |
The table is a triage map, not a substitute for reading the payload. A tag that fires is not proof that GA4 received the right event, and an event in DebugView is not proof that every visitor sent it. The first rows are what you check by hand; the last row is where Trackingplan, a digital analytics agent, works: on the hits real traffic sends, per event, per property, and per platform.
Define the expected event before debugging it
Write down what the event should contain before you look at what it actually contains. Keep the specification short enough that a developer, an analyst, and a QA reviewer can share it.
For each event, record:
- the business action that counts as success
- the exact GA4 event name
- required and recommended parameters
- allowed values and types
- the implementation source:
dataLayer,gtag.js, SDK, or server-side request - the destination and Measurement ID
- the conditions under which the event should not fire
- how to prove the event arrived
Use Google’s recommended event names and parameters when one fits. Google recommends generate_lead when a user submits a form or a request for information, add_to_cart when a user adds items to a cart, and purchase when a user completes a purchase. The recommended name and its parameters give GA4 more context for reports.
The specification also prevents a common mistake: treating a missing property as a missing event. These are different findings:
Expected event: generate_lead
Observed event: generate_lead
Missing field: form_id
Here the collection path works. The property mapping doesn’t.
Follow the diagnostic sequence
1. Confirm the real business action
Start with the action that should create the event, not the button that seems to represent it.
For a lead form, success is a confirmed server response or a thank-you state; a click on submit is only an attempt. For a purchase, success is a confirmed transaction, not a click on “Pay.” For add to cart, confirm that the cart accepted the item.
A click trigger can fire when validation fails, the request is rejected, or the user leaves before the action completes. If the action never reaches the application state that should produce the event, GA4 is not the first layer to repair.
Check:
- Reproduce the action in a clean session.
- Confirm the application reports success.
- Inspect the
dataLayerpush orgtag()call at that moment. - Compare the observed event name and fields with the specification.
In a GTM setup, a deliberate dataLayer event pushed after the success state is easier to debug than a broad click listener.
2. Use GTM Preview to inspect triggers and variables
If Google Tag Manager is in the path, use Preview and Tag Assistant before touching GA4. Google’s Preview and debug documentation describes browsing the site as if the container draft were live, then inspecting which tags fired, in which order, and with which data.
For the event under test, check:
- Did the expected
dataLayerevent appear? - Did the right trigger match it?
- Did the GA4 event tag fire once?
- Did each variable resolve to the expected value?
- Did the consent conditions allow the tag to run?
- Did the tag run after the
dataLayervalues were available? - Did another tag fire for the same action?
Typical findings at this layer:
purchasefires before the ecommerce object is populated.form_idresolves to an empty string because the variable points to the wrong object.- A trigger listens for
form_submitwhile the application pushesgenerate_lead. - A tag fires on both a button click and a thank-you page.
- A consent condition blocks the tag in one state but not in another.
If the tag doesn’t fire, stay in GTM until you can explain why. DebugView can’t help with a request GTM never tried to send.
3. Inspect the raw browser request
GTM Preview shows what the container did. The Network panel shows what the browser actually tried to send.
Open Chrome DevTools before you reproduce the action, then use the Network panel to inspect the request: headers, payload, initiator, timing, and cookies. Filter for the GA4 collection request and compare it with the event you expected.
Verify that:
- the request goes to the intended GA4 collection endpoint
- the Measurement ID belongs to the intended property
- the event name is correct
- the expected parameters are present
- values are not blank,
undefined, or read from the wrong source - numeric fields are sent as numbers where the specification expects numbers
- the request is not blocked or cancelled
- the initiator points to the tag or code path you intended
This step separates a trigger problem from a destination problem:
| GTM result | Browser request | Diagnosis |
|---|---|---|
| Tag didn’t fire | No request | Trigger, consent, sequencing, or tag configuration |
| Tag fired | No request | Browser blocking, transport, consent, or tag configuration |
| Tag fired | Request with incomplete fields | Mapping or source data |
| Tag fired | Request with the right fields | Move on to DebugView and reporting |
Don’t infer that the payload is right from the GTM interface. The browser request is the evidence of what left the page.
4. Verify receipt in GA4 DebugView
DebugView shows the events and user properties GA4 collects from a selected device in real time, and it needs debug mode. On a test device, Tag Assistant or GTM Preview turns it on. Then open Admin › Data display › DebugView and reproduce the action.
Select the event and inspect:
- the event name
- the timestamp
- every expected parameter and its value
- the current user properties
- whether a second copy of the event arrived
An empty DebugView doesn’t mean the event is absent from all GA4 traffic. Check the debug signal, the selected device, privacy controls, the consent state, and the browser request. Google notes that client-side privacy controls and Consent Mode without the required consent can keep events out of DebugView.
DebugView is built for a focused implementation test, not for a historical audit of every visitor. Save the request or your test notes when you need a lasting record.
5. Separate collection from reporting
If DebugView shows the parameter but a standard report doesn’t, collection may already be working.
Google’s event-parameter guidance is explicit: sending a custom parameter is only the first step. To use it in standard reports and explorations, register it as a custom dimension or metric. A newly registered definition can take time to show up in reports.
Check:
- Is the parameter visible in DebugView?
- Is it an event parameter or an item-scoped ecommerce parameter?
- Is the custom dimension or metric registered with the right scope?
- Does the report use the same event and parameter?
- Has enough processing time passed?
- Is the report filtered by another stream, date range, or audience?
Don’t change the tag to fix a correct payload when the gap is in the reporting configuration.
Use the last confirmed evidence to choose the fix
Once you have checked the five layers, the last piece of evidence that holds tells you what to fix.
| Last confirmed evidence | What to compare next | Likely fix |
|---|---|---|
| The business action succeeds, but no event is created | Application callback, event listener, and dataLayer push | Emit the event from the confirmed success state |
The event is in the dataLayer, but the tag is absent from GTM Preview | Trigger event name, trigger filters, tag sequencing, and consent conditions | Correct the trigger or variable mapping |
| The tag fires, but there is no request | Measurement ID, tag configuration, browser errors, blocking, and consent | Repair the transport or the consent configuration |
| The request exists, but a field is missing | Source object, GTM variable, key spelling, nesting, and type | Correct the mapping or the source payload |
| The request is right, but DebugView is empty | Debug mode, selected device, consent, privacy controls, and GA4 property | Fix the test setup or the destination configuration |
| DebugView is right, but the report is wrong | Custom definitions, scope, filters, processing delay, and dimensions | Register or correct the reporting definition |
| One action creates several requests | Client triggers, enhanced measurement, server-side forwarding, retries, and identifiers | Remove the duplicate path or deduplicate |
The goal is not for every layer to look healthy on its own. It is to connect one business action to one expected event with one valid payload.
Check the events that carry revenue and leads
purchase needs transaction and revenue evidence
Check the transaction identity, the revenue fields, the currency, and the items array together. Google’s ecommerce guidance shows transaction_id, value, currency, and items in the purchase payload, and currency is required when value is set.
gtag("event", "purchase", {
transaction_id: "T_12345",
value: 72.06,
currency: "USD",
items: [
{
item_id: "SKU_12345",
item_name: "Blue Widget",
price: 24.02,
quantity: 3
}
]
});
Check that:
transaction_idis present and stable for the ordervalueequals the intended sum of price times quantitycurrencyis a valid three-letter codeitemsis an array of item objects, not a string or a flattened value- the event fires after the order is confirmed
- a refresh of the confirmation page doesn’t send a second purchase
If purchase appears in DebugView but revenue is wrong, inspect the raw request and the ecommerce object before changing report settings. A purchase with the wrong value is a payload problem, even when the event count looks healthy.
add_to_cart needs item-level context
Google’s ecommerce documentation uses add_to_cart with currency, value, and an items array. Each item needs at least item_id or item_name, and both are recommended. If you send value, send currency.
gtag("event", "add_to_cart", {
currency: "USD",
value: 24.02,
items: [
{
item_id: "SKU_12345",
item_name: "Blue Widget",
price: 24.02,
quantity: 1
}
]
});
When the event count is there but product reports are empty, inspect the nested items structure. An item_id inside an item object is not a top-level event parameter. The tag can fire while the ecommerce payload stays unusable.
generate_lead needs a confirmed submission
Google recommends generate_lead when a user submits a form or a request for information, with value, currency when value is set, and an optional lead_source. Teams often add context such as form_id, form_location, or lead_type. Useful, but no substitute for a reliable success trigger: send the event after the submission succeeds, not when the button is clicked, and register the custom fields you analyze every week.
Diagnose each failure class
Missing event
Trace it in this order:
- Did the business action succeed?
- Did the application emit the expected
dataLayerevent orgtag()call? - Did GTM match the trigger and fire the tag?
- Did the browser send the GA4 request?
- Did the request go to the intended property?
- Did DebugView receive the event?
The first “no” is the layer to investigate.
Missing property
If the event exists, compare the property at three points:
- the source object or
dataLayer - the GTM variable or
gtag()argument - the outgoing GA4 request
If it disappears between the first and second point, fix the variable path or the object shape. Between the second and third, inspect the tag configuration, the parameter mapping, and how the request is built. If it is in the request and in DebugView but not in reports, check custom definitions and scope.
Wrong type
A value can be present and still be wrong. Compare the expected type with the request:
- revenue and quantity are numbers where the specification expects numbers
- currency is a currency code, not a symbol or a localized label
- IDs are stable strings
- arrays stay arrays
Don’t normalize a wrong value in the report. Fix it at the source or in the mapping.
Duplicate event
A duplicate needs an inventory of every path that can send the action:
- click listeners
- form callbacks
- thank-you page loads
- enhanced measurement
- client-side GTM
- server-side forwarding
- retries and queue replays
- app and web implementations sharing a destination
Compare timestamps, initiators, transaction IDs, and releases. A duplicate purchase is a revenue problem even when each copy is valid on its own.
Consent-related suppression
Repeat the same test in each relevant consent state. A page can work in a consented DebugView session and still suppress collection for visitors who haven’t granted consent. Record the consent state next to the event evidence; otherwise “the tag didn’t fire” is an incomplete finding that can send the team to the wrong fix. The Consent Mode in GA4 guide covers the setup.
Add coverage beyond one DebugView session
DebugView answers one question well: what did this test device send right now? It doesn’t answer what production sent.
You need a wider check to catch:
- an event that disappears after a release
- a required property missing on one route or template only
- a failure specific to a browser, device, market, or consent state
- a new event name nobody specified
- duplicates across client and server paths
- a slow decline a single QA session won’t reproduce
For a small implementation, a versioned specification plus release checks, request inspection, and a periodic report review can be enough. When the business depends on many events, properties, destinations, or routes, monitoring real traffic adds evidence that a controlled session can’t.
That is the gap Trackingplan covers. One tag, SDK, or server-side hook, with no schema to define and no rules to write: it learns your baseline from real traffic and compares every hour against it, per event, per property, and per platform. Missing properties, dead tags, duplicate fires, broken triggers, type mismatches, and vendors firing before opt-in are caught on real traffic and posted with an explanation and links to the affected hits. It only observes the data your site or app already sends. The GA4 monitoring guide explains how rules, anomalies, and reconciliation fit together.
It doesn’t replace GTM Preview for an unpublished container or DebugView for a focused receipt check. Each tool proves a different part of the path.
The checklist for the next investigation
- Define the business action and the expected event name.
- List required, recommended, and custom parameters.
- Reproduce the action in a clean session.
- Confirm the application emits the event.
- Inspect the
dataLayerorgtag()values. - Use GTM Preview to check triggers, variables, consent, and duplicates.
- Inspect the browser request.
- Confirm the Measurement ID and the destination.
- Verify the event and parameters in DebugView.
- Check custom definitions and report scope.
- Repeat under the consent states, browsers, devices, and routes that matter.
- Add production monitoring when the risk goes beyond the test session.
FAQ
Why does my GA4 event appear in GTM Preview but not in GA4?
GTM Preview proves that the container fired a tag. It doesn’t prove that the browser sent a valid request or that GA4 received it. Inspect the Network panel for the collection request, then check the Measurement ID, the consent state, browser blocking, and the DebugView setup.
Why is a GA4 parameter visible in DebugView but missing from reports?
A custom parameter can be collected without being available as a report dimension or metric. Register it as a custom dimension or metric with the right scope, then allow time for it to appear. Also check that the report uses the same event and date range.
How can I find duplicate GA4 purchases?
Inspect every client, server, page-load, callback, and retry path that can send purchase. Compare request timestamps, initiators, transaction IDs, GTM firing history, and the order confirmation flow. A thank-you page that can be refreshed and client plus server forwarding are the usual suspects.
Can DebugView prove that all visitors are tracked correctly?
No. DebugView validates one debug device over a recent window. It is the right tool for focused testing, but it doesn’t cover every browser, device, route, consent state, app version, or release. Add release checks or real-traffic monitoring when those conditions matter.
Stop where the evidence diverges
The fastest reliable sequence is business action → dataLayer or gtag() → GTM Preview → browser request → DebugView → reporting configuration. Stop at the first layer where the evidence diverges from the specification, fix the cause there, and rerun the same test: by hand on one device, and with Trackingplan on the traffic you can’t reproduce. For broader GA4 problems, the GA4 troubleshooting guide and the event tracking guide cover setup and governance.