---
cuppy_hub: gcp
cuppy_path: guides/identity/fix-permission-denied.md
cuppy_section: docs
cuppy_doc: MHSM-NJWZ
cuppy_doc_uuid: c4ac8096-ef2e-4413-930d-9551e5fb76c3
cuppy_permalink: https://cuppy.io/gcp/d/MHSM-NJWZ
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/gcp/docs/guides/identity/fix-permission-denied
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: d23522f0f9461790ee922b59e43411dcdf1ddd2420da00a5f91d3bb7592c21c6
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Fix a permission denial

## Step 1 — read three facts off the error, then retry **once**

A Google Cloud permission error carries an **error ID** whose context is "the principal, resource,
permission". Those three are the whole input to every step below. Write them down before you touch a policy.

Then retry the identical call once. The IAM API is eventually consistent and a recent grant "might take time
to affect access checks". A single retry costs nothing and resolves a measurable share of denials with no
change at all.

## Step 2 — ask the platform why, instead of guessing

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

`PERMISSION` is the `service.resource.verb` string from the error, not a role. The tool evaluates allow
policies, deny policies and Principal Access Boundary policies together, which is the only way to see an
interaction between them.

**If the answer is `Unknown`, stop.** It means the principal running the diagnosis cannot read one of the
policies involved. It does not mean the policy is absent. Re-run as a principal that can read the
organization's policies.

## Step 3 — is a role missing, or is one being cancelled?

The page's rule: *"all policy types must permit access."* So there are two different repairs and they look the
same from the caller's side:

| Troubleshooter says | Repair |
|---|---|
| no allow policy grants it | **add** a role binding (step 6) |
| a deny policy blocks it | the binding will not help — deny overrides allow. The deny policy must change, and that is almost always somebody else's decision |
| a Principal Access Boundary makes the resource ineligible | same: an outer policy, not yours to widen |
| a conditional binding evaluated false | read the condition. The usual causes are a time window, a resource-name prefix, or a tag the resource does not carry |
| group membership unverifiable | the principal is in a group the evaluator cannot resolve; bind the principal directly to test, then fix the group |

Three of those five are *refusals working as intended*. Widening them is operator-hand work, not a fix.

## Step 4 — check the inherited grants before you add one at the leaf

Policy inherits **down** the hierarchy: organization → folder → project → resource. A principal may already
hold the role one level up, in which case your failure is not the one you think it is. Look upward before
adding a binding at the leaf — and remember that nothing at a child level can take a parent's grant away.

## Step 5 — the `actAs` class, which is not a missing role on the resource

If the permission in the error is about a **service account** rather than about the resource you were touching
— anything of the `iam.serviceaccounts.*` shape — the denial is about identity attachment:

| You are trying to | Role the caller needs |
|---|---|
| create a resource that **runs as** a service account | `roles/iam.serviceAccountUser` on that service account |
| mint a credential **as** a service account | `roles/iam.serviceAccountTokenCreator` on it |

They are different grants on a different object (the service account's own allow policy, not the project's).
A Cloud Run deploy failing with "User does not have permission to access namespace" is usually this.

## Step 6 — grant the narrowest predefined role, at the narrowest scope

Find the role that carries the permission (`reference/identity/predefined-roles-and-permissions.md`), then bind
it as low in the hierarchy as the caller's work allows — on the bucket, not the project; on the project, not
the folder.

## Step 7 — confirm, and record what it was

Re-run the original call. Then record which permission was missing: a migration estimate's accuracy depends
on this list existing (`guides/parity-and-migration-method.md` uses it as an input).

## Never

1. **Never grant a basic role** (`Owner`, `Editor`, `Viewer`) to close a denial. The IAM overview calls them
   "highly permissive, unsuitable for production" and they grant every service rather than the one that failed.
2. **Never write the policy twice on the first failure.** The second read-modify-write on a stale `etag` is how
   an unrelated binding disappears.
3. **Never answer a 401 `authError` with a role grant.** That one is the credential, not the policy.


---
<!-- 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 -->
