This is the pain point many hit with AWS S3 read-only roles. The policy looks correct. The IAM role has s3:GetObject and s3:ListBucket. You expect it to work across all paths. But S3 access control is subtle. One missing permission can block a role from listing or reading even though it can technically fetch objects.
The first trap: ListBucket without the right Resource scope. If the bucket ARN lacks the trailing slash or object wildcard (arn:aws:s3:::bucket-name/*), the role can’t list. The second trap: object-level read-only still fails if AWS KMS encryption is used. Without kms:Decrypt, a role can read the object metadata but not the content. The third: access points and bucket policies. If a bucket policy denies anonymous listing, that deny overrides any role permission.
These pain points grow when teams spread buckets across accounts. Cross-account read-only depends on both sides granting matching ListBucket and GetObject privileges. A single deny on the resource policy is final. Roles set for auditing or testing often lose range because they don’t include all necessary actions tied to S3’s internal API calls.