S3 "Invalid Principal in Policy" (MalformedPolicy): Fix

Logeshwaran
—

The S3 error MalformedPolicy: Invalid principal in policy means that a bucket policy names an IAM user, role, AWS account, or service principal S3 cannot accept. Fix the Principal value by copying the actual ARN or account ID, then validate the document before saving. The counterintuitive trap is a deleted-and-recreated IAM role: even with the identical name, it is a new identity, so an old principal reference can remain broken. A syntax checker alone cannot repair that identity mismatch.

⚡ Quick Answer

• Console: S3 → Bucket → Permissions → Bucket policy. Identify the bad Principal and substitute the exact current IAM ARN.

• CLI: Verify with aws iam get-role or get-user, validate with aws accessanalyzer validate-policy, then use aws s3api put-bucket-policy.

• Extra checks: Deleted identities, wrong role paths, cross-account IDs, and opt-in Region status. Using star merely to make the policy save opens the bucket to everyone.

See the complete repair example or the IAM trust-policy variant.

Jake owns a small phone-repair shop and keeps customer pickup records in a private S3 bucket. A partner has been helping with a new booking system, and Jake wants to let one reporting role read an exports folder. He has already budgeted $40 for a short local promotion, but the deployment is blocked by an invalid-principal error. The example is illustrative; the fix depends on matching the actual IAM identity, not on Jake’s story.

His policy looks ordinary: a single Allow statement, an S3 action, and a role ARN. The API still refuses to save it. A quick correction in the Principal field is likely to matter more than the rest of his infrastructure configuration. His first job is to prove exactly which role and account he meant.

Why S3 rejects a policy with Invalid principal in policy

You paste a bucket policy, select Save changes, and receive Invalid principal in policy instead of a confirmation. S3 has evaluated the Principal element and cannot accept at least one identity represented there. The outer AWS CLI response can display MalformedPolicy, which makes a principal-reference problem look like a generic JSON mistake. The practical fix begins with the identity named in Principal, not with adding more S3 actions.

A principal is the person, role, AWS account, service, or other supported authenticated identity to which a resource policy applies. A bucket policy is a resource-based policy attached to a bucket, so it specifies both actions and who may perform them. A role or user name alone is usually insufficient when you intend to name a particular IAM identity. The ARN, or Amazon Resource Name, establishes exactly which identity you mean.

Sometimes the offending principal was deleted rather than mistyped. Sometimes the policy was copied from another account and still contains the old account ID. A malformed role path, an IAM group ARN, or an instance profile ARN can also be the culprit. A statement with several principals can be rejected because just one member of that array is invalid.

Preserve the current working policy before you edit it, especially on a production bucket. A broad replacement can change more permissions than your one intended fix. Narrow the error to the failed statement, inspect each principal, and repair the smallest possible element. The JSON may be perfectly parseable while the principal remains invalid.

Jake: "I copied a bucket policy from another project. Why does the editor refuse to save it?"

Ethan: "The bucket may be fine. One of the identities in your copied policy is not a real, supported principal for this bucket."

Read the precise error before editing the JSON

When an S3 API request fails, the exact error code is your first useful clue. For a general-purpose bucket, a rejected principal commonly produces MalformedPolicy: Invalid principal in policy. That wording points to principal validation and can appear even when every bracket and comma in the document is correct. Keep the original response rather than relying only on a console banner.

The phrase MalformedPolicyDocument is distinct and commonly appears with IAM policy operations, including some role trust-policy updates. The important distinction is not that one message always means syntax and the other always means identity. You should read the full service message and determine which service API rejected which policy type. Policies for a bucket and policies for an IAM role are evaluated in different contexts.

If the error is AccessDenied while saving, first confirm the caller may update the bucket policy. That is a permissions issue for the person or role saving the document. If the policy saves but a request to S3 later returns AccessDenied, examine identity permissions, bucket policies, block public access, ownership, and applicable guardrails. A saved policy is not proof that the intended user can access every object.

For CLI work, save the error output with its request ID and timestamp. For a CloudFormation stack, open the failed resource event to see the wrapped service error. Terraform and CDK also surface the underlying API error somewhere in the failed operation. You want the innermost S3 or IAM message, not merely an outer deployment status.

Use Principal formats S3 can accept

You need the identity format that matches what you actually want to authorize. An AWS account can be represented by its twelve-digit account ID or the account root ARN. A specific IAM user requires the full user ARN, and a particular IAM role requires its full role ARN. An AWS service principal uses a Service key and its documented service name.

The account root ARN used as Principal is easily misunderstood. In this context it delegates authority to the AWS account, not only to a person signing in as its root user. The account administrator controls which identities receive permission within that account, subject to the rest of the authorization evaluation. This format is useful when the other account manages its own roles.

For multiple principals of one type, supply an array under the AWS key. Those principals are evaluated as alternatives: one authenticated request is not required to be both users simultaneously. Keep a specific role ARN when you want only that role. Use an account principal when that broader delegation is actually intended.

The exact partition also matters. Standard commercial AWS ARNs commonly begin with arn:aws:, while other AWS partitions use their corresponding prefixes. Copy the ARN from IAM rather than guessing. Service principals can vary by service and context, so use the service documentation for the particular S3 integration.

Principal typeSupported exampleMeaning
Account ID{"AWS":"111122223333"}Delegates to account
Account root ARN{"AWS":"arn:aws:iam::111122223333:root"}Account principal
IAM user{"AWS":"arn:aws:iam::111122223333:user/Alice"}Specific user
IAM role{"AWS":"arn:aws:iam::111122223333:role/Reader"}Specific role
Service{"Service":"cloudtrail.amazonaws.com"}Named AWS service, context-specific
All principals"Principal":"*"Everybody, subject to other controls

Spot formats that look plausible but are invalid

An IAM group is a way to attach permissions to a collection of IAM users. It is not an authenticated principal that signs requests to AWS. Therefore arn:aws:iam::111122223333:group/Analysts does not belong as a bucket-policy Principal. If you want multiple users to read the bucket, grant the permissions through the group in their own account or name valid identities in the resource policy.

An instance profile is also not the IAM identity assumed by an EC2 workload. An instance profile is a container for an IAM role associated with an EC2 instance. Use the role ARN as your Principal, not an ARN ending in :instance-profile/.... This is a frequent confusion when an instance profile and role share the same friendly name.

A bare username such as "Principal":{"AWS":"Alice"} is not the reliable full identity representation needed for a specific IAM user. Equally, "role/Reader" by itself lacks the account and ARN structure. Wildcards inside a specific role or user ARN are not valid shortcuts for all matching users. Choose either specific ARNs or a broader supported principal with restrictive conditions.

Account numbers are twelve digits, and a single transposed digit changes the principal. The root segment in an account principal is different from a role named root, and a role path is part of its ARN. Those details are not cosmetic. Copy valid IDs and names from the source account, rather than making the policy look visually consistent by hand.

Pause here, because this is the step that saves you an afternoon. You will not guess the ARN; you will ask IAM for it and paste what it returns. If you take only one habit from this page, take this one.

Find the actual IAM role or user ARN before saving

When the console says a role is invalid even though it definitely exists, the simplest explanation is often that you are looking at the wrong role. AWS accounts can each contain a role named Reader. The display name alone does not identify which account owns it. Compare the exact ARN returned by IAM with the string embedded in the S3 bucket policy.

In the IAM console, open Roles, choose the relevant role, and copy its ARN from the summary. For a user, open IAM Users and copy the user ARN. The caller who owns the role can use aws iam get-role or aws iam get-user. The returned Arn field is preferable to manually reconstructing a string from a screenshot.

Be careful with path components. A role created by an AWS service may have a path such as service-role/. For example, arn:aws:iam::111122223333:role/service-role/ReportReader is different from arn:aws:iam::111122223333:role/ReportReader. The short name may appear identical in a search result.

If the principal is in another account, that account owner can supply its ARN and confirm the role still exists. Your local IAM console will not display every remote account role. Resist substituting the IAM identity currently running your CLI, which may be the bucket owner rather than the intended remote principal. Verify the recipient independently.

aws sts get-caller-identity
aws iam get-role --role-name ReportReader --query 'Role.{Arn:Arn,RoleId:RoleId,Path:Path}'
aws iam get-user --user-name Alice --query 'User.{Arn:Arn,UserId:UserId}'

Understand why deleting and recreating the same role breaks trust

You may be surprised when a role with the correct name exists but a policy referencing its earlier identity no longer works. IAM identities have stable-looking names and unique internal identifiers. When an IAM role or user is deleted and recreated, the new entity receives a different unique identifier. The name is not a proof that it is the old identity.

IAM role trust policies that name a specific role ARN are internally tied to the trusted role principal ID. While the role exists, the console normally shows the friendly ARN. After deletion, the reverse mapping can fail and the stored ID becomes visible. Recreating the role with the old name does not make the previous trust automatically apply to the new ID.

Bucket policies can similarly contain unresolved unique principal identifiers after an IAM identity is deleted. An identifier beginning with a pattern such as AROA for roles or AIDA for users is a clue that a prior identity may no longer resolve. There is no point pasting the raw unique identifier back into a new Principal element as a workaround. Replace it with the current valid ARN of the intended identity.

This behavior helps prevent a newly created identity from automatically inheriting old grants solely by reusing a name. It also means deployment systems that replace roles may need a matching policy update. Treat unexpected role recreation as a security review event, not merely a naming issue. Confirm the new identity has the permissions and ownership you intended.

Jake: "But the new role has exactly the same name as yesterday."

Ethan: "It has a new identity under the label. Replace the stale principal reference with the new role ARN and review who should have access."

Repair the recreated principal without widening access

Start by determining whether the missing identity was intentionally deleted. If the role was retired, removing its statement may be safer than reconnecting the policy to a replacement. If a new role is legitimately taking over, obtain that role’s ARN from its owner. Compare the new role ID with the old record if your deployment history preserves both.

In the bucket policy, find the exact Principal entry associated with the stale ID or missing ARN. Change only that element to the new valid ARN. Review the statement’s actions and resources at the same time, because an inherited broad grant might no longer fit the replacement role. Keep other unrelated statements untouched.

If the problem occurs in an IAM role trust policy, open the target role’s Trust relationships and edit the trusted principal there. IAM roles can use sts:AssumeRole to establish who may assume them. A trust policy is not the same as the target role’s permissions policy, which governs what the role may do after assumption.

A more durable pattern sometimes uses aws:PrincipalArn conditions to match role ARNs, but that changes the semantics of deletion and recreation. Conditions using role ARNs skip that conversion to principal IDs, so a recreated role with the same ARN can regain access. Use such a pattern only when you explicitly intend that persistence and understand its permission-boundary implications.

Cross-account policies need a real account and valid identities

For cross-account sharing, first identify who owns the bucket and who will access it. The bucket owner writes the resource policy; the remote account controls its IAM identities. The remote account must exist, and any specific user or role ARN you name must resolve to a valid identity. A twelve-digit number with a typo is not fixed by granting additional S3 actions.

If you trust the entire remote account, the account ID is a supported Principal. That does not automatically mean every user in the remote account can immediately read every object. Cross-account access normally also depends on permission for the requesting identity in that remote account and any applicable explicit denies. The request must satisfy both sides of the authorization arrangement.

For a least-privilege integration, prefer a specific workload role with only the required bucket actions and object paths. The bucket policy can permit that role’s ARN, and the remote account can configure its workload identity. So that future replacements stop breaking things unexpectedly, agree on role lifecycle ownership with the other account. This is often more important than the initial JSON syntax.

If you cannot confirm the remote account or principal exists, stop before broadening the policy. Replacing the invalid ARN with a wildcard may make the JSON save, but it changes a targeted cross-account trust into access for all principals. A successful Save action is not a security success if the policy now shares customer files publicly.

Check opt-in Regions when the cross-account ARN is valid

One especially frustrating situation is a remote role that exists and has the correct ARN, but the bucket policy still reports an invalid principal. Check whether the bucket is in a Region that is disabled by default in the trusted account. For those opt-in Regions, the trusted principal’s account may need to enable that Region before S3 can accept the policy.

This check is relevant after basic identity validation, not before it. Confirm the account ID, role ARN, and role path first. Then inspect Region opt-in status with the appropriate administrator in the remote account. A bucket being accessible in your account does not prove the other account enabled the same optional Region.

If the remote account cannot enable the Region because of organizational controls or operational requirements, reconsider the cross-account design with its owner. You may be able to use a supported access pattern in a Region both parties operate in, but that choice has data residency and architecture implications. Moving data simply to make the error disappear solves nothing.

Keep the distinction between IAM being a global identity service and S3 requests being Region-dependent. The role can be a real IAM identity while an opt-in Region prerequisite still blocks the bucket-policy update. This is why a valid copied ARN can fail in one environment and work in another.

Grant a whole AWS account access without naming each user

When a partner owns several rotating workload roles, it may be easier to delegate permission to the partner account. Use the twelve-digit account ID in the AWS Principal element or the account root ARN. The root ARN in this position denotes the AWS account principal; it does not mean only the root-login user may receive permissions. The remote account still controls which identities can use those delegated permissions.

For example, you can grant s3:GetObject on objects under a reports prefix to account 111122223333. The statement should reference arn:aws:s3:::example-reports-bucket/reports/* for the objects. If you also intend to allow bucket listing, that is a separate action on the bucket ARN, with a prefix condition if appropriate. One generic resource ARN does not correctly express both operations.

Account-level delegation can be useful when the other account creates roles frequently, because you are not pinning the bucket policy to every specific role ARN. The tradeoff is broader trust. Ask the partner to control its identity permissions and review the bucket policy periodically. If only one trusted workload needs access, a role-level principal is usually simpler to audit.

For highly constrained sharing, you can pair a broad principal pattern with conditions such as an organization identifier or principal ARN. Conditions are powerful, but each changes the accepted request population. Test the intended principals and denial cases through IAM Access Analyzer and representative access requests. A string that resembles an account ID is no guarantee that the right guardrails apply.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "PartnerReportsRead",
    "Effect": "Allow",
    "Principal": {"AWS": "111122223333"},
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::example-reports-bucket/reports/*"
  }]
}

Know what Principal star really permits

If your policy contains "Principal":"*", the statement addresses all principals, including anonymous requests where applicable. It is not shorthand for all users of your current AWS account. Paired with Allow and object-read permissions, it can make objects eligible for public access unless other controls prevent it. This is often dramatically broader than the intention behind an invalid-principal fix.

The overall result still depends on the policy Effect, Action, Resource, conditions, and security controls. An explicit Deny with Principal star can apply a restriction broadly. An Allow with Principal star and a restrictive condition might be intentionally designed for certain requests. Look at the complete policy before describing the statement as public or private.

S3 Block Public Access settings may reject or neutralize public-policy behavior, depending on the setting and request. Those controls leave wildcard trust as wildcard trust; they cannot narrow it to one role. You should design a correct Principal from the start and use Block Public Access as another security layer, not as an excuse to save an unsafe policy.

For a private sharing case, replace a mistyped role ARN with the correct role ARN or the intended account principal. Substituting star just to satisfy the parser is the one move to rule out. The fastest-looking workaround can have the highest long-term cost because it changes who can ask for access.

Jake: "If I put a star there, will it mean everybody in my company?"

Ethan: "No. It means all principals. Use an account or role ARN when you mean a specific business identity."

Jake: "Should I remove every permission and start over?"

Ethan: "No. Identify the one incorrect principal, back up the complete policy, and preserve unrelated statements."

You have seen the rules; now you get to watch one rejected policy become an accepted one, in order. Keep your own policy open beside it, and you will probably find your mistake at the same step Jake found his.

Worked example: fix a rejected bucket policy in order

Imagine a bucket named example-reports-bucket in account 444455556666. Your partner account 111122223333 runs an IAM role called ReportReader. You want that role to read objects under the reports prefix, but Save changes displays Invalid principal in policy. This example uses fictional accounts and bucket names and demonstrates a diagnostic sequence, not an observed production incident.

First, inspect the broken statement without changing any Actions or Resources. Its Principal is arn:aws:iam::111122223333:role/service-role/ReportReader. Ask the partner to look up its actual role ARN. The result is arn:aws:iam::111122223333:role/ReportReader, which shows that the service-role path was mistakenly included.

Second, replace the principal with the exact returned ARN. Keep the action limited to s3:GetObject and the resource limited to objects under the intended prefix. Validate the JSON and run IAM Access Analyzer before saving. Finally, apply the policy in the S3 console or through put-bucket-policy, then test from the partner role with the appropriate identity policy in place.

If the correction still fails, compare the account ID, verify the role was not recreated during the troubleshooting window, and inspect opt-in Region status. Switching to an account-wide principal is a broader access change, so discuss it first. Each step addresses a different failure class, so the test result should guide which step comes next.

Jake: "Can we fix the path without rebuilding the bucket?"

Ethan: "Yes. Keep the same object permission and replace only the incorrect principal ARN."

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "ReadReports",
    "Effect": "Allow",
    "Principal": {
      "AWS": "arn:aws:iam::111122223333:role/ReportReader"
    },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::example-reports-bucket/reports/*"
  }]
}
  1. Identify the bucket-owning account and the trusted account.
  2. Copy the actual role ARN from IAM GetRole in the trusted account.
  3. Correct only the Principal value in the statement.
  4. Validate the JSON and policy, then save with appropriate permissions.
  5. Test reading the approved reports prefix from the trusted role.

Validate a bucket policy before pressing Save

A policy can parse as JSON and still violate IAM policy rules or reference an identity S3 will not accept. Start with local JSON validation to catch commas, quotes, and structural mistakes. Then use IAM Access Analyzer policy validation for semantic findings. Finally, confirm the real principal’s ARN and existence, because a validator cannot substitute for reviewing the intended identity in the correct account.

In the S3 console, open the bucket, choose Permissions, and edit Bucket policy. Review Access Analyzer findings in the editor where available, including errors and warnings. The findings can catch policy design problems before you apply the policy. A clean set of findings is useful, but S3 performs its own checks when the API request is submitted.

The CLI validator accepts a file using --policy-document file://policy.json, with policy type RESOURCE_POLICY and S3 bucket resource type AWS::S3::Bucket. Review all findings, not only errors. If the policy might make the bucket public, use the dedicated no-public-access check as an additional step. This is especially helpful when someone proposes star as a workaround.

After a policy saves, inspect the resulting policy through S3 and test a permitted request from the actual intended principal. A successful policy write does not prove the remote identity has its own required permissions or that an object uses the expected ownership and encryption configuration. Treat validation as one stage of authorization testing, not the entire test.

python -m json.tool policy.json > /dev/null
aws accessanalyzer validate-policy   --policy-document file://policy.json   --policy-type RESOURCE_POLICY   --validate-policy-resource-type AWS::S3::Bucket
aws accessanalyzer check-no-public-access   --policy-document file://policy.json   --resource-type AWS::S3::Bucket
  1. Save a copy of the existing policy and label its revision.
  2. Check JSON syntax locally.
  3. Validate the resource policy using IAM Access Analyzer.
  4. Inspect any potential public-access warnings.
  5. Apply the full corrected policy and conduct positive and negative access tests.

Update and recover the bucket policy using the AWS CLI

When you prefer repeatable changes, keep the complete corrected policy in a version-controlled JSON file. Before updating the bucket, retrieve its current policy and store a backup in a secure location. That lets you compare each Principal entry and makes rollback possible if the intended access behaves differently after the update. Take special care with production buckets shared by multiple teams.

Use aws s3api get-bucket-policy for a bucket that already has a policy. The CLI can extract the policy string to a file that you review and modify. Use aws s3api put-bucket-policy to submit the corrected complete document. This operation replaces the bucket policy, so leaving out an old statement can unintentionally revoke existing permissions.

Where practical, add --expected-bucket-owner to guard against changing a bucket in an unintended account. Confirm the effective caller identity before applying the change. The AWS CLI role currently signed in might be different from the Principal you are granting access to. Those identities serve completely different purposes.

If the CLI says AccessDenied instead of Invalid principal, inspect the caller’s permission to update that bucket policy. A cross-account caller ordinarily cannot update another account’s general-purpose bucket policy even when its IAM policy appears permissive. Work from the bucket-owning account with appropriate permissions. For directory buckets, different API and ownership restrictions can apply, so use their specific instructions rather than blindly copying a general-purpose bucket example.

aws sts get-caller-identity
aws s3api get-bucket-policy   --bucket example-reports-bucket   --query Policy --output text > backup-policy.json
aws s3api put-bucket-policy   --bucket example-reports-bucket   --policy file://policy.json   --expected-bucket-owner 444455556666

Jake: "Would the same error happen if someone wanted to assume an IAM role?"

Ethan: "Yes. A trust policy also names principals, but its permission concerns assuming the role rather than reading S3 objects."

Fix the same invalid principal on an IAM role trust policy

When the failed editor is IAM Roles rather than S3 Buckets, the policy you are changing is the role trust policy. Its Principal identifies who may assume the role, and a typical permission action is sts:AssumeRole. The phrase invalid principal can therefore refer to a broken trusted user or role ARN instead of a broken bucket reader. The service context changes the remedy.

Open IAM, Roles, select the role being assumed, and choose Trust relationships. Inspect the list of trusted identities. If the referenced role or user was deleted, it may appear as an unresolved unique principal ID. Replace that ID with the valid current ARN of the intended trusted identity, or remove the obsolete trust if the identity should no longer assume the role.

For a correct new cross-account trusted role, verify the account, role path, and ARN from the source account. A trust relationship alone is not the complete authorization story: the calling identity also needs appropriate ability to call STS AssumeRole, subject to guardrails. If the role can be assumed but then cannot read S3 objects, debug that downstream authorization separately.

After fixing the trust policy, inspect the saved document and perform an authorized AssumeRole test from the intended source identity. IAM may translate a role ARN into its unique principal ID internally. A newly recreated role is a new identity even when the friendly name matches. A wildcard trust principal is a shortcut around that deliberate security binding, which is exactly why you should rule it out.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "AWS": "arn:aws:iam::111122223333:role/DeployRole"
    },
    "Action": "sts:AssumeRole"
  }]
}
aws iam update-assume-role-policy   --role-name TargetRole   --policy-document file://trust-policy.json
aws accessanalyzer validate-policy   --policy-document file://trust-policy.json   --policy-type RESOURCE_POLICY   --validate-policy-resource-type AWS::IAM::AssumeRolePolicyDocument

MalformedPolicy and MalformedPolicyDocument are not interchangeable

Your troubleshooting depends on both the error text and which API returned it. S3 identifies an invalid bucket-policy principal with the MalformedPolicy error code. IAM may report MalformedPolicyDocument during trust-policy operations for a malformed or unacceptable document. A deployment framework can wrap either error in its own message.

The word malformed is no guarantee that you simply missed a closing brace. Invalid account principals and unresolved IAM role identifiers can produce a rejection even though the JSON syntax is valid. Conversely, a parser error can arise before the service meaningfully evaluates your intended ARN. Separate syntax validation from principal identity validation.

An S3 policy uses an S3 bucket or object ARN in Resource and names who may access it. An IAM trust policy applies to a role and normally expresses which principal may call STS AssumeRole. Swapping those document types creates different failures and may produce confusing wording. Use the corresponding IAM Access Analyzer resource type when validating each document.

If you use CloudFormation, inspect the failed AWS::S3::BucketPolicy or AWS::IAM::Role resource to locate the actual rejection. Fixing the right policy is more useful than rewriting the entire template. After a successful update, verify access from the intended caller and ensure other access remains unchanged.

SignalLikely layerFirst check
MalformedPolicy: Invalid principal in policyS3 bucket policyPrincipal ARN/account existence
MalformedPolicyDocumentIAM policy / trust updateFull IAM error detail and trust policy
AccessDenied during PutBucketPolicyWriter permissionss3:PutBucketPolicy and bucket ownership
AccessDenied during GetObjectRequest authorizationCaller identity, both policies, guardrails

When valid JSON passes validation but S3 still refuses it

Seeing no syntax errors in your JSON editor is an encouraging first result, but it is not enough. The remaining issue might be a nonexistent IAM role, a deleted-and-recreated user, a typo in a twelve-digit account number, or a role path copied incorrectly. Check the complete ARN with its owner. Even a one-character difference makes it a different identity.

Next review principal types. IAM groups and instance profiles are not acceptable authenticated principals in a bucket policy. If your policy references a service principal, confirm the service name and any required conditions against that specific service integration. Service-related policies can be more restrictive than a generic AWS role example.

For cross-account principals in an opt-in Region, ask the remote account to check Region enablement. If the principal resolves and the Region is enabled, inspect whether some other principal in an array is stale. Split large statements into smaller diagnostic groups only in a controlled testing environment, and keep temporary broad permissions unsaved during the investigation.

Finally consider tool state. Your CLI profile may point to another account, an infrastructure template variable may contain an old ARN, or the policy generated during deployment may differ from the file you reviewed. Capture the exact rendered policy submitted to S3. That is the document AWS evaluates, not necessarily the original template source.

Avoid repairing one principal by opening the entire bucket

The most dangerous apparent fix is replacing a bad specific principal with "*". The new statement might save because star is a recognized principal, but its audience changes from a named identity to all principals. If that statement allows object reads, you could expose information well beyond the intended partner account. Do a security review before accepting a policy that broadens access.

Use the narrowest reasonable object prefix and operation. For a reporting reader, s3:GetObject on reports/* is easier to reason about than s3:* on the entire bucket. Add s3:ListBucket only if the workload needs listing, and scope that operation appropriately. Keep writes separate from reads unless the workflow specifically needs them.

When replacing a role, consider whether the new role’s owner is the same trusted team. A familiar ARN is not proof that the operational responsibility remained constant after recreation. Ask who can assume the role and which workloads now use it. For cross-account permissions, that conversation is part of the access review.

Use IAM Access Analyzer to look for public and external access findings, and review S3 Block Public Access settings. The policy grammar can tell you whether a document is accepted, but security depends on who can exercise the grant. Prefer a meaningful denial result in a negative test over a successful save of an unintended permission.

Keep IaC deployments from recreating invalid references

The same error can recur when CloudFormation, Terraform, or CDK creates or replaces roles and bucket policies across stacks. A deployment output containing a role ARN is safer than a copied literal only when it references the correct role lifecycle and account. If the role was replaced, the dependent policy may need to be updated. An unchanged name is not sufficient to prove that the reference still points to the intended identity.

Within a single CloudFormation stack, you can use a role ARN attribute rather than hand-typing the role path. Across stacks, pass an exported output or an explicit parameter with a controlled update process. In Terraform, reference the role ARN from the corresponding resource or data source. For external accounts, treat the external role ARN as a versioned integration contract.

Dependencies solve ordering, not principal correctness. A resource created after an invalid role ARN is supplied will still fail. Conversely, a freshly created role may need time for identity information to propagate before another service evaluates it. If a deployment fails only immediately after creation, a controlled retry can be appropriate once you have ruled out typos and deleted identities.

Review the rendered infrastructure plan before applying changes to production buckets. Watch for changes to Principal arrays, account IDs, and policy statements, not only resource names. Saving the earlier policy snapshot gives you a practical audit trail. Treat an unexpected wildcard or account-wide Principal as a deployment blocker until its business scope is explained.

Know what validation does not guarantee

IAM Access Analyzer validation catches a variety of policy grammar and security issues, but it is not a guarantee that an external principal still exists in every remote account. You should verify the target identity with the account owner. S3 ultimately evaluates the actual policy in the context of its bucket and can reject a principal even when your local JSON parser is satisfied.

A valid Principal also does not guarantee object access. Authorization can depend on both accounts, explicit denies, resource policies, KMS key permissions for encrypted objects, and S3 ownership settings. These are separate troubleshooting branches. Keep the problem statement precise, or you can spend hours modifying a policy that already saved correctly.

For public-read scenarios, a policy allowing anonymous reads can still conflict with Block Public Access settings. For cross-account scenarios, the destination principal may need an identity-based Allow, depending on the access path. For service integrations, the request context may need conditions that limit which source resource can call the service. Different workloads need different safe configurations.

A good final check is a positive and negative test from known identities. The intended role should perform only the permitted operation, while an unrelated role should fail. Compare actual responses with your documented intent. This is stronger evidence than reading a green validation message in isolation.

A decision table for the fastest next action

You can find a single invalid principal without rewriting every policy statement. Work from the symptom toward the lowest-risk test. For a copied policy, inspect its account ID and role path first. For a previously working integration, investigate deletions and role replacement.

If a specific role definitely exists, ask its owner for the actual IAM GetRole ARN rather than a remembered name. If a unique ID is visible, treat that as a possible stale identity link. If the rejection is Region-specific with a cross-account principal, investigate opt-in Region status. Those targeted checks are faster than broad IAM policy changes.

The right solution can be to remove an obsolete principal instead of replacing it. If a partner no longer requires access, a retired entry should not be resurrected under a new role. If account-wide delegation is intentional, document who governs the partner account’s identities. The scope of access should be a decision, not an accident caused by a save error.

After each controlled correction, revalidate before applying. A failed call should leave you with a narrower set of causes than the previous attempt. Keep a copy of the API error and policy revision you sent. Your future self will appreciate being able to tell exactly which principal changed and why.

What you seeLikely explanationNext safe action
ARN has unexpected service-role pathWrong role ARNCopy Arn from GetRole
AROA or AIDA unique IDDeleted IAM principalReplace with current intended identity
Group or instance-profile ARNUnsupported Principal typeUse role/user/account principal
Different account numberWrong AWS accountVerify cross-account target
Only opt-in Region failsRegion not enabled remotelyCheck principal account Region
Star makes save workBroader access substitutedRevert and restore narrow principal

Jake: "The policy saved. Is the access problem finished?"

Ethan: "Now test from the real partner identity. Saving the policy and authorizing the actual S3 request are separate checks."

What to collect before escalating the error

If you have confirmed the ARN, account, principal type, and Region prerequisites but S3 still rejects the policy, gather the smallest reproducible document. Include the full service error message, bucket Region, timestamp, relevant Principal statement, and redacted account identifiers where necessary. Keep sensitive object data and private credentials out of the case. Support investigations rarely need the contents of the bucket.

For a cross-account role, record the GetRole output fields for Arn, RoleId, and Path provided by its owner. If there was a deletion or recreation event, note when it occurred and whether any policy shows an unresolved principal ID. For a trust-policy case, record which role’s Trust relationships were being edited and the underlying IAM API error.

If the policy update is made through a deployment system, attach the rendered policy and the failed resource event. A template variable can differ from the value shown in the source code, so the resolved Principal is what matters. Include the current and intended versions of the statement. Keep unrelated secrets and tokens out of diagnostic logs.

Once the corrected policy is accepted, perform a limited access test from the intended principal and record the result. If it fails at the next stage, distinguish the new AccessDenied from the old invalid-principal rejection. A successful correction is not a promise of unrestricted S3 access, and it should not be treated as one.

Frequently asked questions about S3 invalid principals

What does S3 Invalid principal in policy mean?

S3 rejected one or more identities named in the Principal element of your bucket policy. The role, user, account, or service may be missing, mistyped, deleted, or represented in an unsupported form. Inspect the ARN and account rather than adding new S3 actions.

How do I fix MalformedPolicy Invalid principal in policy?

Copy the actual IAM principal ARN, replace the incorrect Principal value, and validate the complete bucket policy. Check deleted roles, account numbers, and opt-in Regions for cross-account grants. Submit the corrected policy only after reviewing its access scope.

Can an IAM group be a Principal in an S3 bucket policy?

No. IAM groups are collections used to manage user permissions and are not authenticated principals. Use a supported IAM role, user, or account Principal instead. Grant group members identity permissions through an IAM group policy when appropriate.

Can I use an instance profile ARN as Principal?

No. An EC2 instance profile is not the IAM role identity that signs AWS requests. Use the associated IAM role ARN, including the correct role path. Verify the role ARN in IAM before saving.

What does Principal star really allow in an S3 policy?

A star denotes all principals, including anonymous requests where applicable. The effective access still depends on the policy effect, action, resource, conditions, and S3 security controls. An Allow statement with star can expose objects publicly and should not replace a mistyped role ARN.

Why does AWS say my IAM role is invalid when it exists?

You may have the wrong account number, role path, partition, or spelling in the ARN. The role might also have been deleted and recreated, leaving a stale unique principal reference. Ask its owner to return the exact Arn field from GetRole.

How do I find the correct ARN for an IAM role?

Open IAM Roles and copy the ARN from the intended role, or run aws iam get-role --role-name ROLE. Check the Account ID and Path fields as well. A matching short role name in another account is not the same identity.

Why does recreating a role with the same name break a policy?

The recreated IAM role receives a new unique identity identifier. Policies that were tied to the old principal ID treat the replacement as a stranger. Update the policy to reference the new intended role and review its permissions.

Can I grant bucket access to a whole AWS account?

Yes. Specify the remote twelve-digit account ID or its root ARN as an AWS principal. This delegates to the account rather than only its root-login user. Access also depends on the remote identities being authorized and on applicable security controls.

Does cross-account S3 access require the target account to exist?

Yes. The AWS account Principal must identify an existing account, and specific IAM user or role ARNs must identify valid identities. A mistyped account ID can make the policy fail. Confirm account ownership with the receiving team.

Why does my bucket policy fail only in one AWS Region?

For opt-in Regions, the external principal account may need to enable the bucket Region before the policy is accepted. First rule out a wrong ARN or deleted identity. Ask the external account owner to check that Region’s opt-in status.

What is the difference between MalformedPolicy and MalformedPolicyDocument?

MalformedPolicy is an S3 error code used for invalid bucket-policy principals. MalformedPolicyDocument is used in IAM policy contexts, including some trust policy failures. Read the full message and the API that returned it rather than assuming both always indicate JSON syntax.

How do I validate an S3 bucket policy before saving?

Use local JSON validation and IAM Access Analyzer validate-policy with RESOURCE_POLICY and AWS::S3::Bucket. Review findings and consider a no-public-access check. Verify the principal existence separately because JSON validity alone cannot prove that an IAM role exists.

How do I update the bucket policy with AWS CLI?

Save the complete corrected JSON and run aws s3api put-bucket-policy --bucket BUCKET --policy file://policy.json. Back up the existing policy first because the API replaces it. Use expected-bucket-owner when appropriate to guard against targeting the wrong account.

How do I fix Invalid principal in an IAM role trust policy?

Open the role Trust relationships and inspect the trusted IAM user or role ARN. Replace stale unique IDs or deleted-role references with the valid current identity. Validate using AWS::IAM::AssumeRolePolicyDocument and ensure the trust policy grants the appropriate STS action.

Will a valid bucket policy guarantee cross-account object access?

No. The requesting identity may also need IAM permissions and can be subject to explicit denies or additional resource restrictions. Encryption and object ownership can affect access as well. Test the intended request from the actual remote identity after saving.

If this policy rejection stopped a real deployment, keep the original error and your last working policy until the repaired statement is applied. Ask the owner of the intended IAM principal for its exact current ARN, and keep the bucket grant no broader than the business task requires. I hope the next Save action is uneventful—and, more importantly, grants access only to the people and services you intended.

📌 If you keep one line from this page

An S3 principal must identify the intended existing identity; a familiar name, valid JSON, or wildcard shortcut is not enough.

Revision note. Written October 15, 2026, with the Principal formats S3 accepts and the recreated-role trap documented. A name alone makes no one a person, says a Tamil proverb; IAM feels the same about a role recreated under the old name.

Related