July 10, 20266 min read

Google Analytics Showed 22 Users, Then 0. We Spent an Afternoon Proving Nothing Was Wrong.

Free spatial notes

Put this workflow on an infinite canvas instead of another linear doc.

Start in the browser with no account. Create an account only when you want to sync what you made.

The Symptom

Google Analytics realtime was showing periods with a healthy number of active users, then periods with none. The pattern repeated. It looked like something was intermittently breaking.

When your own analytics look broken, everything downstream becomes untrustworthy — you cannot evaluate a marketing change, a deploy, or a funnel experiment. So it was worth an afternoon.

What We Checked

Where the tag comes from. Analytics is not hardcoded anywhere in our application. It is injected by our edge tag manager. That immediately narrows the search: if the tag is not in our bundle, our bundle is unlikely to be breaking it, though it could still block it.

Whether the tag manager is reachable. We fetched the tag manager script directly on both the marketing domain and the app subdomain. Both served correctly, and both returned a valid analytics configuration with the same measurement ID.

Whether our error handling was interfering. This was the most plausible internal suspect. We have global error handlers that suppress console noise from third-party scripts. If one of those were doing more than suppressing logs — cancelling an event, swallowing a rejection that mattered — it could break the tag while looking innocent.

We read them. They suppress logging only. They do not prevent execution and do not cancel anything.

Whether headers were blocking it. No content security policy or header rule in the repository blocks either the tag manager or the analytics endpoint.

Whether the site was intermittently failing. We ran repeated synthetic checks:

  • Marketing domain: 20 of 20 requests returned 200
  • App domain: 20 of 20 returned 200
  • Tag manager script on both domains: 20 of 20 returned 200
  • Marketing routes — home, pricing, blog, tools, demo, sitemap, robots: all 200
  • App shell, manifest, favicon: 200. The authenticated API endpoint returned the expected 401 when unauthenticated
  • Both production builds passed

No intermittent failures.

The Most Likely Explanation

The screenshot may simply be normal, and this is the part worth internalizing.

Realtime analytics reports over different windows. A "last 30 minutes" count of 22 is entirely compatible with a "last 5 minutes" count of 0. Traffic arrived in a burst 6 to 30 minutes ago, and nobody is on the site right now. Both numbers are correct and they describe different things.

For a site with our traffic profile — a few hundred sessions a day, arriving unevenly — realtime is expected to alternate between some users and no users. It is not a smooth line. At low volume, realtime analytics is closer to a stream of individual arrivals than a rate.

Realtime is also the noisiest report available. Ad blockers, privacy-focused browsers, tracking protection, and consent restrictions all suppress events. A meaningful share of visitors never appear in realtime at all, and that share varies with where the traffic came from — technical audiences block far more than general ones.

What We Could Not Verify

Two things, stated plainly because an investigation that reports only what it proved is not an honest investigation.

The tag manager dashboard configuration. Everything we could inspect from code and from the live site was correct, but the configuration itself lives in a dashboard. The things to check there — whether analytics is enabled for both domains, whether the trigger covers single-page-app navigation as well as initial pageview, whether any consent or rule filter intermittently excludes traffic — cannot be verified from a repository.

The single-page-app trigger is the one most likely to cause real problems. If only initial pageviews fire and in-app navigations do not, a visitor who lands and browses registers one event and then vanishes, which produces exactly the sparse realtime pattern we were looking at.

Production error telemetry. We wanted to query our own client error logs. The API token we had was not authorized for that database. So we could not rule out intermittent client-side errors from real user sessions.

That is a gap, and the honest conclusion is that we found no evidence of a problem rather than that we proved there is none. Those are different claims and the difference matters. Reporting "everything is fine" when you could not check one of the most relevant sources would have been wrong.

What We Took Away

Know the shape of your own traffic before diagnosing your analytics. At low volume, realtime alternating between some and none is the expected behavior, not a symptom.

Read the windows on the report. "Last 30 minutes" and "last 5 minutes" disagreeing is not a contradiction.

Rule out your own code first, and read the handler rather than assuming. Our error suppression looked suspicious and was innocent. It took five minutes to confirm.

Fix credential access before you need it. The most valuable check in this investigation was the one we could not run. An expired or under-scoped token is not a problem until it is the thing standing between you and an answer.

Say what you could not verify. An investigation with a stated gap is more useful than a confident all-clear, because the reader knows where to look if the symptom recurs.

Try the product the analytics were arguing about — no account required.

30 days free. No credit card required.

Try the Brainstorm Web template in OmniCanvas

Open a local canvas with the Brainstorm Web layout ready to go — no setup or account needed.

Use the Brainstorm Web Template

Start locally, then sign up only to sync — or explore the interactive demo first.