Debug Log #1: I Fixed the Same Error Twice in One Afternoon, and It Still Wasn't Fixed

I fixed the exact same error twice in one afternoon, in the exact same place, and it still wasn't fixed. The first fix worked -- I could tell because the error message changed. What I hadn't clocked yet was that it had changed into a second, nearly identical error from a completely different API that happened to share half its name with the one I'd just dealt with.

Setting the stage

I was wiring up a script to pull real visitor numbers from Google Analytics (GA4) instead of checking the dashboard by hand -- the same pattern I'd already used for Blogger and Search Console. The script does two things: first it asks Google's Analytics Admin API which GA4 property is connected to the account, then it asks the Analytics Data API for the actual visitor numbers on that property. Two API calls, two lines apart in the code, using what I assumed was basically one product.

The first wall

The very first run failed before it got anywhere near real data:

google.api_core.exceptions.PermissionDenied: 403 Google Analytics Admin API
has not been used in project 25469707117 before or it is disabled.

Straightforward enough -- a GCP project needs each API it calls individually switched on, the same as I'd already hit with Text-to-Speech and Blogger earlier in this project. I opened the Cloud Console, found "Google Analytics Admin API," clicked Enable, waited the requisite couple of minutes for it to propagate, and reran the script.

The second wall, wearing the first wall's clothes

The property lookup succeeded this time -- genuine progress, the script moved past the line that had failed before. Then it died one function call later, with what looked, at a glance, like the exact same error come back to life:

google.api_core.exceptions.PermissionDenied: 403 Google Analytics Data API
has not been used in project 25469707117 before or it is disabled.

My first reaction was that the Enable click hadn't actually taken, or that Google's "wait a few minutes for it to propagate" note was doing more work than the couple of minutes I'd given it. It took an actual side-by-side read of both error messages to notice they name two different services: analyticsadmin.googleapis.com the first time, analyticsdata.googleapis.com the second. Not a retry of the same failure -- a second, separate permission gate, for a second, separate API, that Google happens to package under the same "Analytics" umbrella in its own documentation and even in the same Python library (google-analytics-data and google-analytics-admin ship as two distinct PyPI packages, which in hindsight was the tell I skimmed past).

Why this one is easy to misread

Admin and Data sound like two names for the same thing, and functionally, in this script, they're two halves of one task -- find the property, then read from it. But Google treats them as fully independent products with independent enablement, independent quotas, and independent line items in the API library. Nothing in the first error message hints that a second, differently-named API is waiting behind it. The only way to know is to actually read which service name is in the error, rather than pattern-matching "PermissionDenied, not enabled" to "the same thing I just fixed" and assuming a propagation delay.

The actual fix

Once I'd read the second message properly, the fix was identical in shape to the first: open the Cloud Console, search for "Google Analytics Data API" specifically, click Enable, wait, rerun. No code changed at all -- the script had been correct the entire time. Both walls were pure GCP configuration, and both were one click each. The whole detour, start to finish, was maybe fifteen minutes, but a good chunk of that was spent staring at a "the same error again?" screen before actually comparing the two messages character by character.

If I'm setting up any Google API integration that touches more than one product under a shared brand name again, I'm reading the exact service string in the error before assuming a retry will fix it -- analyticsadmin and analyticsdata look close enough at a glance that I nearly debugged the wrong problem twice in a row.

Related reading

Comments

Popular posts from this blog

My OAuth Tokens Kept Expiring Every 7 Days, and the Reason Was a Dropdown Labeled 'Testing'

How I Wired Up Fully-Automated Cross-Posting Between Blogger and dev.to (With Working Code)

I Tried to Build a Free AI Shorts Channel. Here's Why I Pivoted to Writing Instead.