Three ways a GA4 event count lies to you

Event counts drift for reasons that never show up in the interface. Here are the three most common, and the query that tells them apart.

Sample article. The text is placeholder content written to show how the page renders — headings, code, tables and quotes.

A stakeholder asks why conversions dropped 12% last week. The interface gives you a number and no way to interrogate it. Before you build a story, rule out the three ways the count itself can be wrong.

1. The same action counted twice

Duplication rarely doubles everything. It doubles one path — usually the one that runs through a retry.

One browser action reaches GA4 twice: once through the client-side tag and once through the server container, producing two rows for a single purchase.

One action, two paths, two rows. Neither path is wrong on its own.

Compare events per session rather than total events:

sql
SELECT
  event_date,
  COUNT(*) / COUNT(DISTINCT CONCAT(user_pseudo_id, ga_session_id)) AS events_per_session
FROM `project.analytics_XXXXX.events_*`
WHERE event_name = 'purchase'
GROUP BY event_date
ORDER BY event_date

If sessions are flat and events per session climbed, nothing grew. Something started sending twice.

When a visitor accepts tracking, queued hits are released at once. They arrive with the timestamp of the release, not of the action. On a day when your consent banner changed, this alone moves the curve.

3. The reference table moved

This one is subtle and it has cost me a week. A join returning zero matches feels like proof of absence. It usually is not:

what you see what it can also mean
0 rows matched the reference snapshot is older than your data
ids in a narrow band you are watching an autoincrement at one moment
counts far below expectation the table is a filtered view, not the raw one

Check the maximum id and the snapshot date of the reference table before concluding anything. A zero match against a stale table is not evidence.

What to do first

Build the full history of the signal before attributing it to any event. Do not inherit the time window you were handed — the question “when did this first appear, ever” is one query, and it reorders everything that follows.