AWSRunbook 0/4 done
AccessDenied

AWS Error AccessDenied: S3 Access Denied

Error✕ Not retryable · fix the payloadHTTP 403 ForbiddenAPI Gateway
TL;DR · 10-second read

The caller's IAM policy, the bucket policy, a KMS key policy or Block Public Access denied the S3 action. Grant the action on the right ARN (bucket vs bucket/*) and check explicit Deny statements.

Identify: does your log match?

What you see · exact log / response
An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/app-task-role/9f1c2e7a is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::acme-uploads/avatars/42.png" because no identity-based policy allows the s3:GetObject action
  1. High

    Policy resource ARN mismatch

    Object actions (s3:GetObject, s3:PutObject) need arn:aws:s3:::bucket/*; bucket actions (s3:ListBucket) need arn:aws:s3:::bucket. Swapping them denies every call.

  2. Medium

    Missing kms:Decrypt for SSE-KMS

    Objects encrypted with a customer-managed KMS key also require kms:Decrypt (and kms:GenerateDataKey for uploads) on that key.

  3. Medium

    Missing key looks like 403

    Without s3:ListBucket, S3 returns AccessDenied instead of NoSuchKey for objects that do not exist.

Fix

Grant object and bucket actions on the correct ARNs

✕ Broken · JSON
{
  "Effect": "Allow",
  "Action": ["s3:GetObject", "s3:ListBucket"],
  "Resource": "arn:aws:s3:::acme-uploads"
}
✓ Fixed · JSON
{
  "Version": "2012-10-17",
  "Statement": [
    { "Effect": "Allow", "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::acme-uploads" },
    { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::acme-uploads/*" }
  ]
}

Object-level actions only match object ARNs. Adding ListBucket also turns misleading 403s for missing keys into 404 NoSuchKey.

Verify: reproduce it on purpose

Reproduce locally · aws
$ aws s3api get-object --bucket acme-uploads --key avatars/42.png /tmp/out.png --profile no-s3-access

Run it against a dev environment to trigger AccessDenied on purpose and test your error handler.

Prevent

  • Check bucket policy and SCPs for explicit Deny.
  • Use IAM Access Analyzer to validate policies.
  • Enable CloudTrail data events to see denied calls.

FAQ

Why do I get AccessDenied on an object that does not exist?

S3 returns 403 instead of 404 when you lack s3:ListBucket, to avoid revealing which keys exist.

My IAM policy allows it — why still denied?

An explicit Deny anywhere (bucket policy, SCP, permission boundary, VPC endpoint policy) wins over Allow. KMS key policies can also block.

Does Block Public Access cause AccessDenied?

Yes, for anonymous requests and when putting public ACLs or policies. Use CloudFront with OAC instead of public buckets.

Related errors

Last verified 2026-10-21 · Source: botocore service models (errors per operation) · Official docs ↗ · ~22,200 searches/mo