AWS IAM MalformedPolicyDocument: 12 Causes & Fixes

Logeshwaran
—

The MalformedPolicyDocument error in AWS IAM means your JSON policy has syntax errors, invalid elements, or structural problems that prevent AWS from parsing it. This guide covers the 12 most common causes, exact fixes, real-world broken and repaired policy examples, and validation tools—whether you're using the AWS Console, CLI, or CloudFormation.

⚡ Quick Answer

• Most common cause → Invalid JSON syntax (missing commas, braces, or quotes)

• Run this first → aws iam get-policy --policy-arn ARN (then check the policy document)

• Validate before deployment → Use the aws iam simulate-principal-policy command or the IAM Policy Simulator

For step-by-step fixes, follow the diagnostic flowchart below. If your error mentions permissions instead of syntax, see AccessDenied on sts:AssumeRole.

Understanding AWS IAM Policy Structure and JSON Syntax

An IAM policy is a JSON document that defines permissions in AWS. The MalformedPolicyDocument error indicates that AWS cannot parse the JSON structure. This often happens when policies are built manually, copied incorrectly, or generated programmatically with errors.

The error can occur in several contexts: when creating a policy via the AWS Console, when attaching policies to users/groups/roles, during CloudFormation deployments, or when using the AWS CLI. The exact error message may vary slightly depending on where it occurs, but the root cause is always invalid JSON.

🙋‍♂️ Jake's Reality Check

"I copied a policy from Stack Overflow and only changed the resource ARN. Why is AWS rejecting it?"

Ethan's take: Because JSON is unforgiving, Jake. A missing comma after a changed value will break the entire structure. It's not enough to change the ARN—you have to ensure the surrounding syntax remains valid.

The 12 Most Common Causes (and Their Fixes)

Cause Where It Occurs Primary Fix
1. Invalid JSON Syntax All contexts Use JSON validator, fix commas/braces
2. Missing Required Fields Policy creation Add Version, Statement fields
3. Invalid Effect Value Statement blocks Use "Allow" or "Deny" (case-sensitive)
4. Incorrect ARN Format Resource elements Verify ARN structure: arn:partition:service:region:account-id:resource
5. Unsupported Action Action elements Check action exists in service namespace
6. Invalid Condition Condition blocks Use valid condition operators and keys
7. Version Field Issues All policies Use "2012-10-17" (or "2008-10-17" for legacy)
8. Duplicate Statement Policy document Remove duplicates or use unique Sid
9. Character Encoding Non-English environments Ensure UTF-8 encoding, no BOM
10. Quota Limits Large policies Split policy or remove unnecessary statements
11. CloudFormation Formatting CFN templates Use Fn::ToJsonString or proper YAML
12. Copied Policy Artifacts Copy-paste scenarios Check for smart quotes, special characters

1. Invalid JSON Syntax: The Most Common Culprit

JSON is a strict format. A single missing comma, misplaced brace, or unmatched quote will cause the entire policy to be rejected. Common syntax errors include:

  • Missing commas between statements or between key-value pairs
  • Extra commas after the last element in an array or object
  • Mismatched brackets or braces
  • Unclosed strings (missing closing quote)
  • Using single quotes instead of double quotes (JSON requires double quotes)
  1. Copy your policy JSON into a validator like https://jsonlint.com/ or use the built-in AWS Console editor (it highlights errors).
  2. Check for common syntax issues:
    • After each "Statement" block, ensure there's a comma if another statement follows
    • Verify all "Effect", "Action", and "Resource" keys are properly quoted
    • Ensure arrays (like "Action": ["s3:GetObject"]) are enclosed in square brackets

2. Missing Required Fields

Every IAM policy must include two top-level fields: "Version" and "Statement". Omitting either will cause a MalformedPolicyDocument error.

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

If you're using CloudFormation, ensure the policy document is properly passed as a parameter or embedded in the template. For more on CloudFormation IAM policies, see IAM role troubleshooting for AssumeRole.

3. Invalid Effect Value

The "Effect" element must be either "Allow" or "Deny"—exactly as written, with initial capitalization. Values like "allow", "ALLOW", or "Permit" will cause the error.

⚠︇ What this actually breaks

Using "Effect": "Deny" for everything can create permission conflicts. Deny statements override allows, so a single deny can block access that seems allowed by other statements. Use deny sparingly for specific exceptions.

4. Incorrect ARN Format

Resource ARNs must follow the exact format: arn:partition:service:region:account-id:resource. Common mistakes include:

  • Missing partition (e.g., using arn:s3:::bucket instead of arn:aws:s3:::bucket—though S3 uses a different format)
  • Incorrect region for services that require region-specific ARNs
  • Missing account ID in cross-account policies
  • Invalid service namespace (e.g., using s3 instead of ec2 for EC2 instances)

For more examples of valid ARNs, check the AWS documentation for each service. If you're working with RDS, see RDS connection troubleshooting for related ARN issues.

Real-World Broken and Fixed Policy Examples

The fastest way to learn JSON syntax is to see broken policies next to their repairs. Here are three scenarios you'll encounter in real AWS work.

Example 1: S3 Read Policy with a Missing Comma

Broken version (fails with MalformedPolicyDocument):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:GetObject"
      "Resource": "arn:aws:s3:::my-bucket/*"
    }
  ]
}

The problem: Missing comma after "s3:GetObject" on line 5. The parser expects a comma between the Action and Resource values.

Fixed version:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/*"
    }
  ]
}

Example 2: EC2 Policy with Invalid Effect Value

Broken version (fails with MalformedPolicyDocument):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "effect": "allow",
      "Action": "ec2:DescribeInstances",
      "Resource": "*"
    }
  ]
}

The problems: Two issues here. The "effect" key is lowercase (JSON keys are case-sensitive, so AWS expects "Effect"), and the value "allow" is lowercase instead of "Allow".

Fixed version:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "ec2:DescribeInstances",
      "Resource": "*"
    }
  ]
}

Example 3: Cross-Account Policy with ARN Errors

Broken version (fails with MalformedPolicyDocument):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::shared-bucket/*"
    },
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::123456789012:role/CrossAccountRole"
    }
  ]
}

The problem: This looks valid, but if your AWS account ID is 210987654321 and you're referencing 123456789012 without that account's trust policy allowing your access, you'll get a different error. The JSON is valid; the permission is wrong. This is the boundary between MalformedPolicyDocument and AccessDenied—see the comparison section below.

Fixed version (if the account ID was simply mistyped):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::shared-bucket/*"
    },
    {
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::210987654321:role/CrossAccountRole"
    }
  ]
}

MalformedPolicyDocument vs. AccessDenied: Which Are You Getting?

These two errors confuse more AWS users than any other IAM pair. They look similar in a log file but mean completely different things.

Aspect MalformedPolicyDocument AccessDenied
What it means AWS can't read your policy—the JSON is broken AWS read your policy fine, but it doesn't grant permission
When it appears When creating or updating the policy When using the policy to attempt an action
Fix approach Fix JSON syntax (commas, brackets, quotes) Add permissions, fix trust policy, or check IAM user policy
Policy was created? No—the policy doesn't exist Yes—the policy exists but doesn't allow the action

✅ How to tell which you have

Read the error message carefully. If it says MalformedPolicyDocument, your JSON is broken. If it says AccessDenied or is not authorized to perform, your JSON is fine but permissions are missing. For AccessDenied on role assumption specifically, see both sides of the trust.

The Diagnostic Flowchart: Find Your Failure Point

Follow this path to systematically identify where the JSON is failing:

Step What to Check Command or Action If It Fails, Look At
1 Basic JSON validation aws iam get-policy --policy-arn ARN JSON syntax errors (Cause #1)
2 Required fields Check for "Version" and "Statement" Missing fields (Cause #2)
3 Effect values Check for "Allow" or "Deny" Invalid Effect (Cause #3)
4 ARN format Verify ARN structure Incorrect ARN (Cause #4)
5 Action validity Check action exists for service Unsupported Action (Cause #5)
6 Condition syntax Check condition operators Invalid Condition (Cause #6)

CloudFormation-Specific Issues and Expanded Troubleshooting

When embedding IAM policies in CloudFormation templates, you must use the Fn::ToJsonString function or ensure proper YAML formatting. Common issues include:

  1. Using YAML shortcuts that produce invalid JSON when converted
  2. Multiline strings that include indentation or trailing spaces
  3. Sub variables that don't resolve to valid JSON values
Resources:
  MyPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Fn::ToJsonString:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action: 's3:GetObject'
              Resource: 'arn:aws:s3:::example-bucket/*'

CloudFormation Gotcha 1: YAML Multiline Syntax

If you embed JSON directly in a YAML template using the | or > multiline operator, CloudFormation may pass literal newlines that break JSON parsing:

# BROKEN - literal newlines in JSON
Resources:
  MyRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: |
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {"Service": "ec2.amazonaws.com"},
              "Action": "sts:AssumeRole"
            }
          ]
        }

This can work in some cases, but trailing spaces or indentation issues will cause MalformedPolicyDocument errors. The safer approach is to use YAML-native syntax:

# FIXED - YAML-native syntax
Resources:
  MyRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole

CloudFormation Gotcha 2: Substitution Failures

When using !Sub or Fn::Sub in policy documents, if a variable doesn't resolve, the JSON will contain the literal placeholder text instead of a valid value:

# BROKEN - ${BucketName} not defined
Resources:
  MyPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Fn::ToJsonString:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action: 's3:GetObject'
              Resource: !Sub 'arn:aws:s3:::${UndefinedVariable}/*'

Check CloudFormation events for substitution errors, then define the variable in Parameters or use a direct string.

Validation Tools and Debugging Techniques

1. AWS IAM Policy Simulator

The IAM Policy Simulator lets you test policy documents before deploying them. It's available in the AWS Console under IAM > Policy Simulator.

  1. Navigate to IAM > Policy Simulator
  2. Select "Paste policy document"
  3. Paste your JSON and run simulation
  4. The simulator will highlight syntax errors and logical issues

2. AWS CLI Validation

You can use the AWS CLI to test policy documents without creating them:

aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:user/testuser \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::example-bucket/*"
      }
    ]
  }'

3. Local Development Tools

For local validation before deployment:

  • JSON Linters: Most IDEs have built-in JSON validators
  • AWS Toolkit: For VS Code, provides IAM policy validation
  • cfn-lint: For CloudFormation templates, validates embedded policies

Preventing Future MalformedPolicyDocument Errors

Prevention Strategy How to Implement Benefit
1. Use Infrastructure as Code Define policies with CloudFormation or Terraform Version-controlled, testable policies
2. Implement Policy Validator in CI/CD Add JSON validation to pipeline Catch errors before deployment
3. Use Managed Policies Where Possible AWS-managed policies for common use cases Pre-tested, maintained policies
4. Regular Policy Reviews Use IAM Access Analyzer Identify policy issues and unintended access
5. Use Policy Generators Carefully AWS Policy Generator or similar tools Valid JSON structure, but verify permissions

Update, October 2026: catch malformed policies before AWS does

The fastest way to fix MalformedPolicyDocument is to never see it in production. Since this guide was written, readers have asked how to validate policies automatically and about a few error variants that the twelve causes above do not name directly.

Validate with IAM Access Analyzer

The IAM policy editor in the console runs IAM Access Analyzer policy validation as you type, listing errors, security warnings and suggestions below the editor, often with the exact line. From the command line, the same checks run with:

aws accessanalyzer validate-policy --policy-type IDENTITY_POLICY --policy-document file://policy.json

Use RESOURCE_POLICY for bucket, queue and key policies. The output reports findings by type: an ERROR means AWS would reject the policy, while a SECURITY_WARNING flags something allowed but risky, such as a wildcard that grants more than you intended. Running it before every deployment catches nearly all of the causes listed above.

In pipelines: validate at review time

For infrastructure as code, run policy validation in CI before changes merge. CloudFormation templates can be checked with AWS's open-source policy validator for CloudFormation, which calls Access Analyzer for each policy in the template, and Terraform plans can be checked with the same API on the rendered JSON. Access Analyzer also offers custom policy checks, which can block policies that grant specific sensitive actions or that newly allow public access, turning security rules into automatic review gates.

Error variants worth recognizing

"Has prohibited field Resource" usually means a trust policy, the document that says who can assume a role, includes a Resource element, which trust policies do not allow; remove it and keep Principal. "Policy document should not specify a principal" is the reverse: a Principal in an identity policy attached to a user, group or role, which only resource policies and trust policies may contain. "Syntax errors in policy" is the generic message for invalid JSON, cause 1 above, and an editor with JSON validation finds it instantly. "Invalid principal in policy" often means the ARN of a role or user that was deleted and recreated, since policies reference the unique principal behind the name; update the policy to the current ARN.

Watch size too

Once a policy is valid, the next error large teams hit is a size limit. Our guide to the IAM policy size limit covers the exact limits and how to stay under them.

More questions readers asked in October 2026

How do I validate an IAM policy before applying it?

Use the IAM console's policy editor, which validates as you type, or aws accessanalyzer validate-policy from the CLI.

What does "Has prohibited field Resource" mean?

A trust policy contains a Resource element. Remove it; trust policies use Principal instead.

Why does AWS say my policy should not specify a principal?

Identity policies attached to users, groups or roles cannot contain Principal. Only resource and trust policies can.

Update added October 3, 2026. The original guide above is unchanged.

Frequently Asked Questions

1. How do I find the exact syntax error in my policy?

Use the AWS Console's policy editor (it highlights errors), or copy your JSON into an online validator like jsonlint.com. The AWS CLI also provides specific error messages when you try to create a policy.

2. Can I use single quotes in IAM policies?

No. JSON requires double quotes for all strings, including keys and values. Single quotes will cause the policy to be rejected as malformed.

3. What does the error "Invalid principal in policy" mean?

This is a related error that occurs when the Principal element in a resource-based policy is invalid. It often indicates the same JSON formatting issues, but specifically in the principal section.

4. How do I validate a policy before creating it?

Use the IAM Policy Simulator in the AWS Console, or the aws iam simulate-principal-policy CLI command with your policy document to test it without creating it.

5. What's the maximum size for an IAM policy?

The maximum size for a managed policy is 6,144 characters, and for inline policies it's 2,048 characters. Exceeding these limits will cause the policy to be rejected.

6. Can I use comments in IAM policies?

No. JSON doesn't support comments. Any comment-like syntax (e.g., // or /* */) will cause the policy to be malformed.

7. How do I fix "Missing required fields" in CloudFormation?

In CloudFormation, ensure your policy document includes both "Version" and "Statement" fields. Use the Fn::ToJsonString function to properly convert your YAML/JSON structure.

8. What's the difference between inline and managed policies?

Inline policies are embedded directly in a user, group, or role, while managed policies are standalone policies that can be attached to multiple identities. Both must follow the same JSON structure.

9. How do I troubleshoot policy errors in CloudFormation?

Check the CloudFormation events for the specific error, then use the aws cloudformation validate-template command to check your template. Also ensure your policy documents are properly escaped.

10. Can I use wildcards in resource ARNs?

Yes, but only in specific positions. Wildcards are not allowed in the partition, service, or region parts of an ARN. You can use wildcards in the resource-id part, but be cautious as they can make policies overly permissive.

11. How do I check if my policy is valid using the AWS CLI?

You can use the aws iam simulate-principal-policy command with your policy document. If the policy is malformed, you'll get a specific error message pointing to the issue.

12. What should I do if my policy was working but suddenly stopped?

Check CloudTrail for policy modification events. Someone may have inadvertently changed the policy, or an automated process may have updated it with a malformed version.

13. How do I handle special characters in policy values?

Use JSON escaping for special characters. For example, use \n for newlines, \" for quotes within strings, and \\ for backslashes.

14. Can I use variables in IAM policies?

Yes, IAM supports policy variables like ${aws:username} and ${aws:PrincipalTag}. However, they must be properly formatted and used in supported contexts.

15. How do I export and inspect an existing policy?

Use the aws iam get-policy-version --policy-arn ARN --version-id VersionId command to retrieve the policy document. This will show you the exact JSON that AWS is storing.

16. What's the difference between MalformedPolicyDocument and AccessDenied?

MalformedPolicyDocument means AWS can't parse your JSON—the policy won't even be created. AccessDenied means the policy was created fine but doesn't grant the permission you're trying to use. See AccessDenied troubleshooting for permission issues.

Conclusion: Writing Valid IAM Policies

The MalformedPolicyDocument error is frustrating but almost always solvable with careful JSON validation. By understanding the common causes—invalid syntax, missing fields, incorrect values—you can quickly identify and fix issues.

Remember that IAM policies are strict JSON documents. Always validate before deployment, use tools like the Policy Simulator, and implement infrastructure as code to catch errors early in your development process.

For readers preparing for an AWS certification

We have been covering AWS certifications alongside our fixes, so here is a short extra for readers studying for one. If you only came for the fix, you already have it; feel free to skip this part.

Exams that test this topic: Security – Specialty (SCS-C03) • Developer – Associate (DVA-C03 from December 1, 2026).

Here are the exam-style questions this topic tends to raise, with the reasoning you need to answer them:

1. What causes MalformedPolicyDocument?

Invalid JSON, a missing Version, wrong element names, an invalid ARN, or a Principal element in an identity-based policy.

2. Which Version string should every policy use?

2012-10-17.

3. When is the Principal element allowed?

Only in resource-based policies and role trust policies, not in policies attached to users, groups or roles.

4. Which tool checks policies for errors and security warnings before you save?

IAM Access Analyzer policy validation, also available in the console editor.

5. What is the difference between NotAction and an explicit Deny?

NotAction matches every action except those listed; with Allow it grants broadly, so it is usually paired with Deny for guardrails.

6. How do you use variables such as the user's name in a policy?

Policy variables like ${aws:username}, which require Version 2012-10-17.

Exam codes and dates change often. Our AWS certification changes page keeps track of what is current.

Revision note. Written September 2026, covering AWS IAM as of August 2026. AWS policy syntax and validation tools evolve slowly, but check the official IAM documentation for any updates. If you've been fighting this error, remember that JSON validation is a skill that improves with practice—you've got this.

Related