Skip to main content

AccessDenied When Calling S3 Operations: Fix AWS CLI 403

Fix `An error occurred (AccessDenied) when calling the ListObjectsV2 operation` in the AWS CLI. Read the enhanced 403 message to find which policy denied you.

9 min readUpdated August 2026

When the AWS CLI prints An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied, your credentials worked. S3 authenticated the request, evaluated it against every applicable policy, and refused it.

$ aws s3 ls s3://amzn-s3-demo-bucket
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

That is a fundamentally different failure from Unable to locate credentials, which never reaches AWS at all. Here AWS knows exactly who you are; the question is what you are allowed to do.

Read the Full Error First

For requests made within the same AWS account — and for requests within the same AWS Organization — S3 returns enhanced access denied context that names the policy type responsible. The general shape is:

User: arn:aws:iam::123456789012:user/MaryMajor is not authorized to perform:
s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket/object-name"
because no identity-based policy allows the s3:GetObject action

or, when something actively denied you:

User: arn:aws:iam::777788889999:user/MaryMajor is not authorized to perform:
s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket/object-name"
with an explicit deny in a resource control policy

Two phrases carry the diagnosis:

  • with an explicit deny in a {type} policy — a Deny statement matched. An explicit deny always wins, and no Allow anywhere can override it.
  • because no {type} policy allows the {action} action — an implicit deny. Nothing denied you; nothing granted you either, and IAM denies by default.

The {type} names where to look: identity-based policy, bucket policy, service control policy, resource control policy, or permissions boundary.

Cross-account requests from outside your organization return only a bare Access Denied with no context. Requests denied by a VPC endpoint policy also stay generic, as do requests to directory buckets.

Confirm Who You Actually Are

Before editing any policy, confirm which principal made the call:

aws sts get-caller-identity

A surprising share of these errors are simply the wrong profile — the console session is an administrator while the CLI is running as a limited CI user. If the ARN is not who you expected, switch profiles rather than changing permissions.

Work Through the Causes in Order

1. The action is not granted at all

s3 ls and s3 cp need different permissions, and this catches people constantly:

CommandPermissionResource ARN
aws s3 ls s3://buckets3:ListBucketarn:aws:s3:::bucket
aws s3 cp s3://bucket/key .s3:GetObjectarn:aws:s3:::bucket/*
aws s3 cp file s3://bucket/keys3:PutObjectarn:aws:s3:::bucket/*

The bucket ARN and the object ARN are different resources. A policy granting s3:GetObject on arn:aws:s3:::bucket/* lets you download a known key while ls fails — exactly the confusing split people report.

Grant the specific action on the specific ARN:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::amzn-s3-demo-bucket"
    },
    {
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::amzn-s3-demo-bucket/*"
    }
  ]
}

Resist "Action": "s3:*" with "Resource": "*". It makes the symptom disappear by granting standing access to every bucket in the account, which is a far larger problem than the one you started with.

Advertisement

2. The bucket policy does not allow your principal

For same-account access, either the identity policy or the bucket policy can grant permission. For cross-account access, both sides must allow it: the bucket policy must name your principal, and your identity policy must allow the action. One without the other fails.

3. An SCP or RCP denies it above your account

Service control policies and resource control policies set a ceiling that no account-level grant can exceed. If the error names one, the fix belongs in AWS Organizations, not in your IAM policy — and usually belongs to a different team.

4. Encryption is blocking the request

If the object is encrypted with SSE-KMS, S3 permissions alone are not enough. Reading needs kms:Decrypt; writing needs kms:GenerateDataKey. Both must be allowed by your IAM policy and by the KMS key policy. The failure surfaces as an S3 AccessDenied, which sends people hunting through bucket policies for a problem that lives in KMS. Our S3 encryption guide covers how the two interact.

5. Block Public Access, ownership, or Requester Pays

  • Block Public Access overrides permissive bucket policies and ACLs for anonymous requests. Do not disable it to make an error go away — use a presigned URL or CloudFront with origin access control.
  • Object Ownership / ACLs: an object uploaded by another account may not be owned by the bucket owner. Enforcing bucket owner ownership avoids the whole class of problem.
  • Requester Pays buckets reject requests that do not include --request-payer requester.

6. VPC endpoint policies

When traffic goes through a gateway or interface endpoint, the endpoint policy is evaluated too. This is the case where the enhanced message does not appear, so a stubbornly generic Access Denied from inside a VPC is a strong hint to check the endpoint policy.

Verify the Fix

Re-run the exact failing command, then confirm the grant is scoped as narrowly as you intended:

aws s3 ls s3://amzn-s3-demo-bucket
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:user/dev \
  --action-names s3:ListBucket \
  --resource-arns arn:aws:s3:::amzn-s3-demo-bucket

The policy simulator evaluates the request without performing it, so you can confirm a change works — and confirm neighbouring actions are still denied.

Prevent It

  • Start from least privilege and add named actions as failures identify them. Starting broad and narrowing later almost never happens.
  • Use IAM Access Analyzer to generate policies from CloudTrail activity rather than writing them from guesswork.
  • Keep CloudTrail on. The errorCode and errorMessage fields on a denied event give you the same diagnosis after the fact — see our CloudTrail logging guide.
  • Test with the simulator before deploying policy changes to shared environments.

Frequently Asked Questions

Find answers to common questions

Your request reached S3 and was authenticated successfully, but authorization failed. Some policy in the evaluation chain either explicitly denied the action or simply never allowed it. Unlike a credentials error, AWS knows exactly who you are — the problem is what that identity is permitted to do.

Read the full error message. For same-account requests, and for requests within the same AWS Organization, S3 returns enhanced context naming the policy type. 'with an explicit deny in a resource control policy' points at an RCP; 'because no identity-based policy allows the s3:GetObject action' means nothing granted it. Cross-account requests outside your organization return only a generic Access Denied.

An explicit deny is a Deny statement that matched your request, and it always wins — no Allow anywhere can override it. An implicit deny means no Deny matched but no Allow did either, since IAM denies by default. The error wording distinguishes them: 'with an explicit deny in a...' versus 'because no ... policy allows the ... action'.

They need different permissions. Listing a bucket requires s3:ListBucket on the bucket ARN, while reading an object requires s3:GetObject on the object ARN — arn:aws:s3:::bucket versus arn:aws:s3:::bucket/*. A policy that grants only the second lets you fetch a known key while ls fails.

The console session and the CLI may use different identities. Run 'aws sts get-caller-identity' to see which principal the CLI is actually using, and compare it against the identity you signed into the console with. Different profile, different permissions.

Yes, and it is easy to miss. If the object uses SSE-KMS, you need kms:Decrypt on the key to read it and kms:GenerateDataKey to write it, granted by both your IAM policy and the key policy. The denial surfaces as an S3 error even though the KMS key is what refused.

No. Broad grants turn a specific, diagnosable failure into standing over-permission across every bucket in the account. Read the error to identify the exact action and resource, then grant only that action on only that ARN.

Some AWS errors include an encoded authorization failure message. Running 'aws sts decode-authorization-message --encoded-message ' returns the decoded policy evaluation detail. It requires the sts:DecodeAuthorizationMessage permission, and it applies to encoded messages rather than to the plain S3 403 text.

A VPC endpoint policy is likely restricting it. Endpoint policies are evaluated alongside identity and bucket policies, and note that a denial caused by a VPC endpoint policy does not produce the enhanced error context, so the message stays generic.

Yes, for anonymous and public requests. Block Public Access settings at the account or bucket level override permissive bucket policies and ACLs. The correct fix is almost never to disable them — use a presigned URL or CloudFront with an origin access control instead.