AWS `AccessDenied vs AccessDeniedException`: What It Means and How to Fix It

AccessDenied vs AccessDeniedException is two different denial formats from two generations of AWS API, and what each one tells you. It comes from IAM, and the message reads:

Access Denied / User is not authorized to perform …

If you are here mid-incident, skip to how to tell which cause you have — the command there narrows it down faster than reading the list.

Quick reference

Error AccessDenied vs AccessDeniedException
Service IAM
Message Access Denied / User is not authorized to perform …
Most common cause The identity policy does not allow the action

What it actually means

Both mean IAM denied the request, but they come from different API styles and carry different amounts of detail.

AccessDenied is the older, terser form — S3 is the service most people meet it on, and its bare “Access Denied” says nothing about which principal, action, or resource was involved.

AccessDeniedException is the newer form used by most services released since roughly 2015, and it usually embeds a readable sentence: User: arn:aws:sts::111122223333:assumed-role/my-role/session is not authorized to perform: lambda:InvokeFunction on resource: arn:.... That sentence is the answer — read the principal, the action, and the resource, in that order.

On S3 specifically, the denial can come from any of five places, which is why it is so much harder to diagnose than a Lambda denial.

Why it happens

Ranked by how often each one turns out to be the answer.

# Cause Fix
1 The identity policy does not allow the action The common case. Read the action name from the exception text and add it. For S3, remember that object actions and bucket actions are different resources: arn:aws:s3:::my-bucket for ListBucket, arn:aws:s3:::my-bucket/* for GetObject.
2 A bucket policy denies it S3 evaluates the bucket policy as well as the identity policy. An explicit Deny in either wins over any Allow. Bucket policies that enforce TLS (aws:SecureTransport) or a specific VPC endpoint (aws:SourceVpce) deny everything else, including you.
3 S3 Block Public Access is on It overrides bucket policies and ACLs. A perfectly valid public-read policy still denies if the account-level or bucket-level block is enabled.
4 The object is owned by another account Cross-account uploads without bucket-owner-full-control leave objects the bucket owner cannot read. Enabling S3 Object Ownership set to bucket-owner-enforced prevents this class of problem entirely.
5 KMS, not S3, is the denial A bucket encrypted with a customer-managed key needs kms:Decrypt on that key as well as s3:GetObject. The error surfaces as S3 Access Denied and mentions nothing about KMS. Check the key policy and the identity policy for KMS actions.
6 An SCP or permissions boundary caps it Same as any denial — evaluated above the account, invisible from inside it.

How to tell which one you have

Read the exception text first. If it names the principal and action, you are most of the way there.

For S3, work through the layers in order:

# 1. What does the bucket policy say?
aws s3api get-bucket-policy --bucket my-bucket --query Policy --output text | python3 -m json.tool

# 2. Is Block Public Access on?
aws s3api get-public-access-block --bucket my-bucket

# 3. Who owns the objects, and is ownership enforced?
aws s3api get-bucket-ownership-controls --bucket my-bucket

# 4. Is it encrypted with a customer-managed key?
aws s3api get-bucket-encryption --bucket my-bucket

Then simulate the call as the actual principal:

aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::111122223333:role/my-role \
  --action-names s3:GetObject \
  --resource-arns 'arn:aws:s3:::my-bucket/some/key'

The simulator does not evaluate SCPs from inside a member account, so a “allowed” result plus a real-world denial points at an organisation policy.

Preventing it

Set S3 Object Ownership to bucket-owner-enforced on new buckets — it removes ACLs from the picture and with them an entire class of cross-account denial.

When a bucket uses a customer-managed KMS key, grant kms:Decrypt and kms:GenerateDataKey in the same policy statement as the S3 actions, so the two never drift apart.


Quota codes, limits and behaviour on this page were last checked against AWS documentation on 2026-08-22. AWS changes these; if something here does not match what you are seeing, trust the console and tell us.