AWS S3
BucketOwnerEnforced
Object Ownership
ACLs
Cloud Storage

AWS S3 Bucket cannot have ACLs set with ObjectOwnership's BucketOwnerEnforced setting

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

This error usually appears when you try to apply S3 Object Ownership with BucketOwnerEnforced to a bucket that still has bucket ACL grants. In that mode, ACLs are disabled, so the bucket ACL must effectively be private and give full control only to the bucket owner.

That makes this error different from the related AccessControlListNotSupported upload error. Here, the problem is often the bucket's existing ACL configuration, not just an --acl flag on a single object upload.

What BucketOwnerEnforced requires

With BucketOwnerEnforced, S3 disables ACL-based access control and expects permissions to come from IAM policies, bucket policies, and related policy-based mechanisms.

In practice, that means:

  • the bucket owner owns all objects
  • ACLs no longer control access
  • bucket ACLs must not grant access to external accounts or public groups

If the bucket still has ACL grants such as public read or access for another AWS account, S3 rejects the ownership change.

A common failing scenario

Suppose a bucket already has a public-read bucket ACL or grants access to another account. Then this command fails:

bash
aws s3api put-bucket-ownership-controls \
  --bucket my-bucket \
  --ownership-controls Rules=[{ObjectOwnership=BucketOwnerEnforced}]

The failure happens because S3 cannot disable ACLs while ACL-based permissions are still active on the bucket.

You may also hit the same problem during bucket creation if you try to create the bucket with BucketOwnerEnforced and a conflicting bucket ACL in the same workflow.

Inspect the existing bucket ACL

Before changing ownership controls, inspect the current ACL:

bash
aws s3api get-bucket-acl --bucket my-bucket

If you see grants to groups such as AllUsers or to external canonical users, that is the reason the ownership-controls change is being rejected.

For example, a bucket ACL that grants public read is incompatible with BucketOwnerEnforced:

json
1{
2  "Grants": [
3    {
4      "Grantee": {
5        "Type": "CanonicalUser",
6        "ID": "bucket-owner-id"
7      },
8      "Permission": "FULL_CONTROL"
9    },
10    {
11      "Grantee": {
12        "Type": "Group",
13        "URI": "http://acs.amazonaws.com/groups/global/AllUsers"
14      },
15      "Permission": "READ"
16    }
17  ]
18}

That public READ grant must be removed or translated into a bucket policy first.

Migrate ACL intent into a bucket policy

If the ACL was providing real access, move that access into a bucket policy before enabling BucketOwnerEnforced.

json
1{
2  "Version": "2012-10-17",
3  "Statement": [
4    {
5      "Effect": "Allow",
6      "Principal": {
7        "AWS": "arn:aws:iam::123456789012:role/AppReader"
8      },
9      "Action": "s3:GetObject",
10      "Resource": "arn:aws:s3:::my-bucket/*"
11    }
12  ]
13}

Once the policy is in place, reset the bucket ACL so only the owner retains control, then apply the ownership setting.

Apply the change in the right order

A safe migration usually looks like this:

  1. Read the current bucket ACL.
  2. Convert any needed grants into a bucket policy.
  3. Reset the bucket ACL to private or owner-only.
  4. Apply BucketOwnerEnforced.

Then verify the result:

bash
aws s3api get-bucket-ownership-controls --bucket my-bucket

This staged approach avoids breaking access accidentally while still moving the bucket to the modern policy-based model.

Watch for tooling that adds ACLs

Even after the bucket is fixed, old automation may continue trying to use ACLs. Deployment scripts, SDK wrappers, or legacy Terraform modules sometimes send canned ACLs by default.

Search for values such as:

  • 'public-read'
  • 'bucket-owner-full-control'
  • 'x-amz-acl'
  • 'acl ='

Those settings may not be the source of this exact error, but they often surface immediately after the ownership migration and cause the next wave of failures.

Common Pitfalls

The biggest mistake is assuming this error refers only to object uploads. With this specific error text, the problem is often that the bucket already has ACL grants when you try to enable BucketOwnerEnforced.

Another issue is removing the ACL before translating the access rules into a bucket policy. That can break legitimate access for other principals.

Teams also miss inherited behavior in infrastructure code. A CloudFormation template, SDK helper, or deployment tool may be creating buckets with ACL settings you did not realize were still present.

Finally, do not respond by re-enabling ACLs unless you truly need object-level ACL behavior. For most modern S3 buckets, policy-based access with BucketOwnerEnforced is the cleaner model.

Summary

  • This error usually means you are enabling BucketOwnerEnforced on a bucket that still has ACL grants.
  • Inspect the bucket ACL with get-bucket-acl before changing ownership controls.
  • Move any needed access rules into a bucket policy first.
  • Reset the bucket ACL to owner-only, then apply BucketOwnerEnforced.
  • After the migration, remove ACL usage from automation and client uploads as well.

Course illustration
Course illustration

All Rights Reserved.