---
cuppy_hub: gcp
cuppy_path: reference/identity/errors.md
cuppy_section: docs
cuppy_doc: HBJ7-0Z5T
cuppy_doc_uuid: c569613e-0f73-4aca-a76f-0a485e94944f
cuppy_permalink: https://cuppy.io/gcp/d/HBJ7-0Z5T
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/gcp/docs/reference/identity/errors
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: f2e1627f39987a72757bda8bfe90fb5036a6b2a1205f16f9593c80ca5f9cb2e5
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Identity error catalogue

Every row is **cause → check → fix**. The check is what confirms the cause; run it before changing a policy.

## 1. The refusals

| Signature | Cause | Check | Fix |
|---|---|---|---|
| HTTP 403, reason `forbidden` — "According to access control policy, the current user does not have access" | the allow policy does not grant a role carrying the permission | run Policy Troubleshooter (§2) with the exact permission from the error | grant the predefined role that carries it (`reference/identity/predefined-roles-and-permissions.md`), at the narrowest resource that works |
| HTTP 403, reason `insufficientPermissions` | as above, with the service reporting it per-action rather than per-resource | same | same |
| HTTP 401, reason `authError` | the credential itself: the token is expired or its format is not recognised | re-mint the credential; if the caller federates, confirm the exchange still returns a token | fix the credential path, not the policy — a 401 is never answered by a role grant |
| A permission error carrying an **error ID** | the service is telling you which principal, which resource and which permission | the ID "provides context for the error, including the principal, resource, permission" — read all three off it | the three facts are the whole input to Policy Troubleshooter |
| "User does not have permission to access namespace" on a Cloud Run deploy | the deployer cannot act as the runtime service account, or the default Compute Engine service account has been deleted | check whether the deployer holds `roles/iam.serviceAccountUser` on the runtime account, and whether that account still exists | grant `roles/iam.serviceAccountUser` (not TokenCreator) and pass `--service-account` explicitly |
| HTTP 429, reason `rateLimitExceeded` | a usage limit, not a permission | read the live quota (`reference/overview/quotas-and-limits-model.md`) | back off with truncated exponential backoff, then request an increase if it is adjustable |
| A grant that "did not work", and works a minute later | **not an error.** The IAM API is eventually consistent; a change "might take time to affect access checks" | retry the original call once, unchanged | wait. Do **not** write the policy again — a second read-modify-write on a stale `etag` is how a binding gets lost |

## 2. The one tool that answers "why", verbatim

```
gcloud beta policy-intelligence troubleshoot-policy iam RESOURCE \
    --principal-email=EMAIL --permission=PERMISSION
```

It evaluates every policy type that bears on the call. The page's own rule is the one to hold on to:

> "For a user to be able to use the permission to access the resource, all policy types must permit access."

## 3. The five things it checks, in the order a denial is decided

1. a **Principal Access Boundary** policy makes the resource ineligible;
2. a **deny policy** blocks the permission — and deny overrides allow, so this outranks any role the principal
   holds;
3. the **allow policies** simply do not carry the permission;
4. a **conditional role binding** exists and its condition evaluated **false**;
5. **group membership** could not be verified.

A sixth outcome is the troubleshooter's own: **"Unknown"**, when the caller running it lacks permission to read
one of the policies involved. An `Unknown` is not a pass. It means you need a higher-privileged principal to
run the diagnosis, and treating it as "no deny policy found" is the single most common misread.

## 4. Limits that present as errors

Each of these is fixed; the IAM quotas page states, verbatim, **"Limits cannot be changed."** A call failing
against one of them is not a quota you can raise.

| Limit | Value | How it shows up |
|---|---|---|
| principals in one allow policy | 1,500 | adding a binding starts failing on a mature project; the fix is groups, not retries |
| deny policies per resource | 500 | a policy-as-code rollout stops applying |
| custom roles per project / per organization | 300 each | a generated-role pipeline stalls |
| service account keys per account | 10 | a key rotation that creates before deleting fails on the 11th |
| maximum access token lifetime | 3,600 s, extendable to 12 h by organization policy | a long job dies mid-run; lengthen the lifetime by policy or re-mint |

## 5. Where to go next

- the order to check things in: `guides/identity/fix-permission-denied.md`
- the keyless setup that removes a whole class of 401s:
  `guides/identity/github-actions-without-keys.md`
- the same refusals' AWS and Azure counterparts, and why a migrated workload hits different ones:
  `guides/parity/identity.md`


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 91 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/gcp/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
