Amazon S3 access and request errors

Look up an S3 access or request error by its code or message — every 403 AccessDenied variant S3 emits, and the region, signature, clock and credential errors — each with ranked causes, the check and the fix. Bucket, object, upload and throttling errors are in reference/s3/bucket-and-object-errors.md.

markdownreferencev4VP6N-203Kstage: operatetrust: officialtrust: fieldservice: s3interface: anyS3 AccessDenied403 Forbiddenno identity-based policy allowsno resource-based policy allowsexplicit deny in a resource-based policyPermanentRedirectAuthorizationHeaderMalformedIllegalLocationConstraintExceptionSignatureDoesNotMatchRequestTimeTooSkewedInvalidAccessKeyIdExpiredToken S3HeadersNotSignedBlock Public AccessSSE-C blocked

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

S3 errors carry a machine code (<Code>), a human message, the resource, and a request ID. Search by the code first. Two things change how you read them: a HEAD request has no response body, so you get only the HTTP status, never the code; and S3 gives a detailed access-denied message only when the caller and bucket owner are in the same account or organization. Outside that, it is a bare Access Denied.

403 AccessDenied

S3's detailed form:

User: <principal ARN> is not authorized to perform: <action> on resource: "<resource ARN>" <context>
<context> ends withMeaningGo toCheck
because no identity-based policy allows the <action> actioncaller's own policies lack an Allowreference/iam/errors.mdaws iam simulate-principal-policy --policy-source-arn <caller> --action-names <action> --resource-arns <arn>
because no resource-based policy allows the <action> actionbucket or access point policy lacks an Allow or IgnorePublicAcls ignored the public ACL that would have allowed itreference/s3/access-control.mdaws s3api get-bucket-policy --bucket <b>; aws s3api get-public-access-block --bucket <b>
with an explicit deny in a resource-based policya bucket/access point Deny matched or RestrictPublicBuckets blocked a public policyreference/s3/access-control.mdget-bucket-policy: the Deny statements; aws s3api get-bucket-policy-status --bucket <b>
because public ACLs are prevented by the BlockPublicAcls setting in S3 Block Public Access.the request tried to set a public ACLRemove the ACL; ACLs are usually disabled anywayget-public-access-block → BlockPublicAcls
because public policies are prevented by the BlockPublicPolicy setting in S3 Block Public Access.PutBucketPolicy with a policy S3 classes as publicMake the policy non-public (fixed principals or keys)get-public-access-block → BlockPublicPolicy
because this bucket has blocked upload requests that specify Server Side Encryption with Customer provided keys (SSE-C). Please specify a different server-side encryption typeSSE-C is blocked on the bucket (the default since April 2026)Use SSE-S3 or SSE-KMS, or re-enable SSE-C (reference/s3/encryption.md)aws s3api get-bucket-encryption --bucket <b> → BlockedEncryptionTypes
… service control policy …, … resource control policy …, … VPC endpoint policy …, … permissions boundary …, … session policy …that policy type refused; explicit denies add , with policy ARN: <arn>reference/iam/errors.mdThe ARN in the message; reference/organizations/scps-and-rcps.md

When the message is just Access Denied (cross-organization, VPC endpoint denials within an organization, directory buckets), work through these, most likely first:

  1. Cross-account without both sides allowing. Caller's identity policy and bucket policy must both allow. Check: compare account IDs in the caller ARN and bucket owner.
  2. Object owned by another account. With ACLs enabled (Bucket owner preferred / Object writer), a bucket-policy Allow only covers objects the bucket owner owns. Check: aws s3api get-bucket-ownership-controls, then get-object-acl for the owner. Fix: set Bucket owner enforced (after migrating ACL grants), or re-copy the object.
  3. KMS. SSE-KMS objects need kms:Decrypt to read and kms:GenerateDataKey to write, allowed by the key policy and the caller's policy; an AWS managed aws/s3 key works only for its own account. Check: aws s3api head-object shows the key ID.
  4. Object missing and no s3:ListBucket. S3 answers 403 instead of 404. Check: grant s3:ListBucket on the bucket ARN and retry; a 404 confirms it.
  5. Requester Pays. Add --request-payer requester (CLI) or the x-amz-request-payer: requester header.
  6. Wrong identity or an expired/narrowed session. Check: aws sts get-caller-identity.
  7. Object Lock. Deleting a specific version under compliance retention or legal hold returns 403; governance retention needs s3:BypassGovernanceRetention.
  8. Presigned URL. It carries the signer's permissions and dies with the signer's credentials (guides/s3/share-with-presigned-urls.md).

Request and signing errors

Code (HTTP)Message (S3's wording)Likely cause → fixCheck
AuthorizationHeaderMalformed (400)The authorization header is malformed; the region '<r>' is wrong; expecting '<r2>'Signed for the wrong Region, or endpoint set alongside region (the SDK uses the endpoint). The global endpoint must be signed for us-east-1. Fix: set only region to the bucket's Regionaws s3api head-bucket --bucket <b> → BucketRegion vs the client's Region
PermanentRedirect (301)The bucket you are attempting to access must be addressed using the specified endpoint…Request sent to the wrong Region's endpoint. Fix: use the bucket's Region; SDK for JS v3 can follow it with followRegionRedirects: true at a latency costSame call
IllegalLocationConstraintException (400)…You are trying to access a bucket from a different Region than where the bucket exists…Same family: Region mismatchaws s3api get-bucket-location --bucket <b>
SignatureDoesNotMatch (403)The request signature that the server calculated does not match the signature that you provided. Check your AWS secret access key and signing method…Wrong secret key; a proxy altering headers or query string; presigned URL used with a different method/headers than it was signed for; clock driftaws sts get-caller-identity with the same key; compare the signed headers with those sent
RequestTimeTooSkewed (403)The difference between the request time and the server's time is too large.Fix the system clock (NTP)date -u vs a time server
InvalidAccessKeyId (403)The AWS access key ID that you provided does not exist in our records.Deleted or wrong key: reference/sts/credential-sources.mdaws sts get-caller-identity fails the same way
ExpiredToken (400)The provided token has expired.Session or presigned URL's credentials expired: reference/sts/errors.mdThe session's or presigned URL's X-Amz-Date + X-Amz-Expires
AccessDenied with HeadersNotSigned: if-range—Presigned URL signs Range but not If-Range; add If-Range to the signed headersX-Amz-SignedHeaders in the URL lacks if-range

When none of these fits

Bucket, object, upload and throttling errors: reference/s3/bucket-and-object-errors.md. Collect the RequestId and the exact message, search the hub for the message, then follow guides/s3/fix-403.md for denials. AWS Support can trace a request from its request ID.

Read this document as markdown — the same bytes an agent receives.