Controlled probe — is the step-4 build failure a missing role, or propagation delay?
Why this exists. During Persona B's step 4, gcloud builds submit failed 403 on a brand-new project. I granted three IAM roles and the next attempt succeeded. The obvious write-up would be "the codelab omits a required IAM step." That write-up would have been wrong, and this probe is what caught it.
The tell: the failing service account already held roles/editor, which already confers storage.objects.get — the exact permission the error named. A grant of roles/storage.admin on top of roles/editor cannot be what unblocked it.
Design
Create a second, independent fresh project. Attempt the same operation immediately. If it fails, wait — and retry with no IAM change whatsoever. If it then succeeds, the cause is propagation, not permissions.
Project: cl-dx-probe-0726. Trivial two-line Dockerfile, so the build itself cannot be the variable.
Results
| Time (UTC) | Event |
|---|---|
| 18:46:52 | project created |
| 18:47:28 | billing linked, cloudbuild.googleapis.com enabled |
| 18:47:35 | attempt 1 — FAILED |
| — | no IAM changes made |
| 18:52:20 | attempt 2 — SUCCESS, build completed in 24s |
| 18:52:51 | IAM state re-checked on the default compute SA |
Attempt 1 error:
ERROR: (gcloud.builds.submit) PERMISSION_DENIED: generic::permission_denied:
Permission 'iam.serviceAccounts.get' denied on resource (or it may not exist).
IAM state on the default compute service account, after attempt 2 succeeded:
roles/editor
That is the same single role it had at attempt 1. Nothing was granted. Nothing was changed.
Conclusion
The failure is IAM propagation delay on newly created Google Cloud projects. A new project has a window of roughly 3–5 minutes after creation during which Cloud Build fails with permission errors naming permissions the service account demonstrably already has.
Two independent projects reproduced it, and — importantly — they failed on different permissions:
| Project | Failing permission | Time from project create to failure |
|---|---|---|
cl-dx-b-0726 |
storage.objects.get |
2m 41s |
cl-dx-probe-0726 |
iam.serviceAccounts.get |
0m 43s |
Different checks failing on different runs is the signature of eventual consistency, not of a missing grant. A missing role would fail identically every time.
Why this matters more than the failure itself
The error message prescribes the wrong action. It names a permission, names a principal, and even supplies an IAM Troubleshooter URL. Every signal in it points the reader toward editing IAM policy. The correct action is to wait sixty seconds and retry.
The consequences differ sharply by persona:
- Persona A takes the error at face value, cannot fix IAM (or does not know how), and concludes the codelab is broken or that they lack access. This is a plausible abandonment point.
- Persona B knows how to grant roles, does so, sees success, and draws a false causal conclusion — then potentially writes it into a runbook, propagating a cargo-cult fix. I made exactly this error and only caught it because the pre-existing
roles/editorbinding was visible in the output.
The second failure mode is arguably worse, because it is silent and it scales.
Limits of this probe
- Two projects, one account, one region, one afternoon. Sample of two.
- The exact propagation window was not bisected — the probe waited ~5 minutes and did not retry at finer intervals, so "3–5 minutes" is bounded by observation, not measured precisely.
- Both projects were created by the same account under the same billing account. Behaviour under an Organization with org policies is untested and could differ materially.
- This does not establish that propagation is the only cause of 403s at this stage — only that it is sufficient to produce them without any missing role.