Troubleshooting
Start here:
php craft telescope/analytics/check
It verifies the credentials parse, the token exchange succeeds and the property answers — three things that fail independently, and knowing which one failed saves most of the guesswork.
“That looks like a measurement ID”
You have entered G-XXXXXXX. Telescope needs the numeric property ID from Admin → Property details — the measurement ID belongs to a data stream, not the property, and the Data API will not accept it.
“User does not have sufficient permissions for this property”
The service account email has not been added under Admin → Property access management. Adding it to the Google Cloud project is not the same thing and does not grant Analytics access. Add the ...iam.gserviceaccount.com address to the property as a Viewer.
Check the property ID too, while you are there — the same error appears if the account is a Viewer on a different property than the one you configured.
“invalid_grant … account not found”
The key has been revoked, or it belongs to a service account that has since been deleted. Create a new JSON key and re-add the account to the property.
It can also mean the server’s clock is out of sync. The assertion Telescope signs is timestamped, and Google rejects one whose window does not line up. If the credentials are definitely current, check NTP on the server.
Everything reports zero
Almost always: the page path Telescope is matching is not the path GA4 recorded. Find out what Telescope is asking for:
php craft telescope/analytics/show 1234
Then compare it with what GA4 actually has, under Reports → Engagement → Pages and screens. Common mismatches:
- A trailing slash. Telescope trims it, because an exact match on
/about/misses hits recorded against/about. If your property records the slash, use begins with. - Query strings. By default they are dropped and
/blog?page=2counts towards/blog. Turn on Include query strings if you want them separated. - A path prefix your setup adds — a language segment, say — that GA4 records inconsistently. Contains is the blunt fix.
- Not enough time has passed. GA4 is not real-time in the Data API; a page published an hour ago may genuinely have no rows yet.
Change the setting, then clear the cache so you see the effect immediately.
Several sites, and every number looks too big
Your Craft sites share one GA4 property and Filter by hostname is off, so /about on each of them is the same page path and their traffic is being summed. Turn it on — see Multi-site.
Rate limit or quota errors
A full report is six Data API calls, one per section. If you are hitting quota:
- Switch off report sections nobody reads. This is the biggest single lever.
- Raise the cache duration above the default 600 seconds.
- Warm the cache from cron so editors hit warm reports rather than triggering fresh calls:
telescope/analytics/warm. - Check that a
warmcron job is not itself the thing burning the quota — a large--limitrunning every minute adds up fast.
The report shows some sections and errors on others
That is deliberate. Sections are fetched independently and a failure in one does not discard the rest — the failed section reports its reason instead. Partial data beats a blank panel.
Nothing appears on an entry at all
Check, in order:
- The user has the View analytics reports permission — see Permissions.
- The entry has a public URL. Entries with no URL have no page path, so there is nothing to ask GA4 about and the panel is skipped.
- Either the Analytics field is in that entry type’s field layout, or Show on all entries is on — and if Limited to sections is set, that this entry’s section is one of them.
Still stuck?
Open an issue on GitHub with the output of telescope/analytics/check — with any credentials redacted.