You tried to create or update an IAM policy, a bucket policy, a KMS key policy, or another resource policy, and AWS rejected it with MalformedPolicyDocument. This is a validation error: the policy document does not conform to the policy language, so AWS will not save it. The good news is that these are deterministic and fixable, and the error message usually names the field or element it could not parse. The trap is assuming that because the JSON is well formed it must be a valid policy. AWS checks the grammar and the meaning of the fields, not just the braces.
MalformedPolicyDocument: ...
What this error means
AWS policies are JSON documents with a strict schema on top of the JSON syntax. A policy must have a valid Version, one or more Statement blocks, and each statement must use valid values for Effect, Action, Resource or Principal, and any Condition. MalformedPolicyDocument means the document violated one of those rules: the JSON might be fine, but a field holds something the policy engine cannot accept. Common offenders are ARNs in the wrong format, a Version that is not one of the two accepted date strings, a Principal that does not name a valid entity, a condition operator that does not exist, or a wildcard placed where a full ARN is required. Because it is a structural rejection, no permission or resource state is involved; you fix the document and resubmit.
Common causes
- A missing or invalid
Version(it must be2012-10-17for new policies, or the older2008-10-17). - An
Actionwritten incorrectly (wrong service prefix, or an action that does not exist for that service). - A
ResourceARN in the wrong format, or a wildcard where a full ARN is required. - A
Principalthat is malformed or names an entity type AWS does not accept in that position. - A
Conditionwith a typo in the operator (for exampleStringEqualinstead ofStringEquals) or a non-existent condition key structure. - Trailing commas, wrong quoting, or a single statement where an array is expected (or vice versa) at the JSON level.
How to fix it
STABILIZE
- If this is blocking a deploy, do not keep resubmitting variations blindly. Capture the exact policy document you are trying to apply and validate it before the next attempt, so each fix is deliberate.
DIAGNOSE
- Read the full error message, not just the error code. AWS usually appends what it could not parse (for example a specific ARN or the phrase
Policy document should not specify a principalfor an identity policy). That text tells you which element to fix. - Validate the document with IAM Access Analyzer, which checks policy grammar and reports findings by line with specific messages:
aws accessanalyzer validate-policy \
--policy-type IDENTITY_POLICY \
--policy-document file://policy.json
Use RESOURCE_POLICY as the --policy-type for bucket, KMS, and similar resource policies. The findings name the element and the problem (for example an invalid action or a bad ARN format).
- Confirm the ARN format for the resource type in question. ARNs follow
arn:partition:service:region:account-id:resource, and some services omit region or account. A resource policy on the wrong ARN shape is a frequent cause. - If you want to confirm the semantics of a policy that does validate structurally, simulate an action against it to be sure it does what you intend:
aws iam simulate-custom-policy \
--policy-input-list file://policy.json \
--action-names s3:GetObject \
--resource-arns arn:aws:s3:::my-bucket/reports/2026.csv
FIX
- Correct the field the validator flagged. A minimal, well formed identity policy looks like this, with
Versionset to2012-10-17and a correctly formatted resource ARN:
aws iam put-role-policy \
--role-name app-role \
--policy-name reports-read \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}]
}'
Note that s3:ListBucket acts on the bucket ARN while s3:GetObject acts on the object ARN (/*), a distinction that causes malformed or ineffective policies when mixed up.
- Fix condition operators to their exact names (
StringEquals,StringLike,ArnEquals,IpAddress, and so on); a typo in the operator is rejected. - Ensure identity policies do not include a
Principal(that belongs only in resource and trust policies), and that resource policies do include a validPrincipal. - Re-validate with
accessanalyzer validate-policyuntil it returns no errors, then resubmit.
How to prevent it
- Run
accessanalyzer validate-policyin CI on every policy document before it is deployed. - Keep policies in version control as formatted JSON so diffs make a stray comma or wrong ARN obvious.
- Use policy templates with the correct
Versionand ARN patterns rather than hand-writing each one. - Use the IAM policy simulator to confirm a valid policy also does what you intend, not just that it parses.
- Prefer generated or templated policies (from IaC modules) over manual editing for complex condition blocks.
When to call a senior engineer
Call for help when a resource or trust policy keeps failing validation and the message is not clear, when cross-account or KMS key policies must be exactly right to avoid locking access away, or when a large policy set needs to be validated and corrected across many accounts. Erzon engineers can pin down the exact malformed element, rewrite the policy to least privilege with correct ARNs and conditions, and put validation in your pipeline so malformed policies never reach AWS again.
Related errors we fix
AccessDenied: User is not authorized to perform this operation Fix this error → AWS Stack:arn:... is in ROLLBACK_COMPLETE state and can not be updated. Fix this error → AWS UPDATE_ROLLBACK_FAILED Fix this error → AWS ProvisionedThroughputExceededException: The level of configured provisioned throughput for the table was exceeded Fix this error → Want a senior engineer to just fix it?
Paste this exact error into the form. A senior engineer replies within one business hour with what is likely wrong and a flat quote. Triage is free.
Book a fix