TerraformPilot

DevOps

Fix Terraform Error - JSON Parsing Invalid Character

Learn how to fix JSON parsing invalid character errors in Terraform policies, IAM templates, and external data sources. Includes validation and debugging tips.

LLuca Berton2 min read

Quick Answer

#

Your Terraform configuration contains malformed JSON — typically trailing commas, single quotes, unescaped characters, or a BOM marker. Use jsonencode() to generate JSON from HCL objects, or validate your JSON with jq or python3 -m json.tool before using it.

The Error

#

When running terraform plan or terraform apply, you encounter:

Error: "policy" contains an invalid JSON: invalid character '}' after object key:value pair
Error: Error parsing JSON: invalid character '\'' looking for beginning of value
Error: "assume_role_policy" contains an invalid JSON:
  invalid character ',' after object key:value pair
Error: Error in function call: invalid character 'S' looking for beginning of value

What Causes This Error

#

1. Trailing Commas

#

JSON does not allow trailing commas (unlike HCL or JavaScript):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "*",    // ← trailing comma = INVALID
    },                     // ← trailing comma = INVALID
  ]
}

2. Single Quotes Instead of Double Quotes

#

JSON requires double quotes for strings:

{'Effect': 'Allow'}     // ← INVALID — single quotes
{"Effect": "Allow"}     // ← VALID — double quotes

3. Unescaped Special Characters

#

Strings containing backslashes, quotes, or newlines must be escaped:

{"path": "C:\Users\admin"}       // ← INVALID
{"path": "C:\\Users\\admin"}     // ← VALID

4. BOM (Byte Order Mark) Characters

#

Files saved with a BOM (common in Windows editors) have invisible characters at the start:

\xEF\xBB\xBF{"Version": "2012-10-17"...}

5. Comments in JSON

#

JSON does not support comments:

{
  // This is not allowed in JSON
  "Version": "2012-10-17"
}

6. Terraform Template Interpolation in JSON

#

Using ${...} incorrectly in JSON strings:

# BAD — unescaped interpolation inside JSON
policy = <<EOF
{"Resource": "arn:aws:s3:::${var.bucket}/*"}
EOF

How to Fix It

# #

The best approach — let Terraform generate valid JSON from HCL objects:

resource "aws_iam_policy" "s3_read" {
  name = "s3-read-policy"
 
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid      = "AllowS3Read"
        Effect   = "Allow"
        Action   = [
          "s3:GetObject",
          "s3:ListBucket",
        ]
        Resource = [
          "arn:aws:s3:::${var.bucket_name}",
          "arn:aws:s3:::${var.bucket_name}/*",
        ]
      }
    ]
  })
}

jsonencode() handles:

  • Proper quoting and escaping
  • No trailing commas in output
  • Correct boolean/number types
  • Variable interpolation

Solution 2: Use IAM Policy Data Source

#

For AWS IAM policies specifically:

data "aws_iam_policy_document" "s3_read" {
  statement {
    sid    = "AllowS3Read"
    effect = "Allow"
    actions = [
      "s3:GetObject",
      "s3:ListBucket",
    ]
    resources = [
      "arn:aws:s3:::${var.bucket_name}",
      "arn:aws:s3:::${var.bucket_name}/*",
    ]
  }
}
 
resource "aws_iam_policy" "s3_read" {
  name   = "s3-read-policy"
  policy = data.aws_iam_policy_document.s3_read.json
}

This is more readable, supports conditions natively, and never produces invalid JSON.

Solution 3: Validate External JSON Files

#

If you must use external JSON files:

# Validate with jq
jq . policy.json
 
# Validate with Python
python3 -m json.tool policy.json
 
# Find the exact error location
python3 -c "
import json
try:
    json.load(open('policy.json'))
    print('Valid JSON')
except json.JSONDecodeError as e:
    print(f'Error at line {e.lineno}, column {e.colno}: {e.msg}')
"

Reference validated JSON files in Terraform:

resource "aws_iam_policy" "main" {
  name   = "my-policy"
  policy = file("${path.module}/policies/s3-read.json")
}

Solution 4: Remove BOM Characters

#
# Check for BOM
file policy.json
# Output: "policy.json: UTF-8 Unicode (with BOM) text"
 
# Remove BOM with sed
sed -i '1s/^\xEF\xBB\xBF//' policy.json
 
# Or with vim
vim -c "set nobomb" -c "wq" policy.json
 
# Or recreate without BOM
cat policy.json | sed '1s/^\xEF\xBB\xBF//' > policy_clean.json
mv policy_clean.json policy.json

Solution 5: Fix Heredoc JSON with Proper Escaping

#

If you must use heredoc JSON (not recommended):

# Use jsonencode instead, but if you must:
resource "aws_iam_role" "main" {
  assume_role_policy = <<-POLICY
  {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "ec2.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }
  POLICY
}

Escape Terraform interpolation if needed:

# Double $$ to escape interpolation
policy = <<-POLICY
{
  "Statement": [{
    "Condition": {
      "StringLike": {
        "s3:prefix": "$${aws:username}/*"
      }
    }
  }]
}
POLICY

Common JSON Mistakes Quick Reference

#
MistakeInvalidValid
Trailing comma{"a": 1,}{"a": 1}
Single quotes{'a': 1}{"a": 1}
Unquoted keys{a: 1}{"a": 1}
Comments{"a": 1 // comment}{"a": 1}
Trailing comma in array[1, 2,][1, 2]
Unescaped backslash"C:\path""C:\\path"
Single backslash-n"line\nbreak""line\\nbreak" or actual escaped newline

Troubleshooting Checklist

#
  1. ✅ Are there trailing commas in the JSON?
  2. ✅ Are all strings using double quotes?
  3. ✅ Does the file have a BOM? (file policy.json)
  4. ✅ Are special characters properly escaped?
  5. ✅ Does jq . file.json validate successfully?
  6. ✅ Can you replace the raw JSON with jsonencode()?
  7. ✅ For IAM policies, can you use aws_iam_policy_document?

Prevention Tips

#
  • Always use jsonencode() — it produces valid JSON by construction
  • Use aws_iam_policy_document for IAM policies — HCL syntax with automatic JSON output
  • Validate JSON in CI — add jq . file.json to your pipeline
  • Configure editors to save without BOM — VS Code: "Files: Encoding" → UTF-8
  • Use linterstflint and terraform validate catch some JSON issues
#

Conclusion

#

JSON parsing errors in Terraform almost always come from hand-written JSON with syntax mistakes. The fix is simple: use jsonencode() to generate JSON from HCL objects, or use provider-specific data sources like aws_iam_policy_document. If you must use raw JSON files, validate them with jq before referencing them in your config.

#Terraform#Troubleshooting#DevOps#Error Fix#Infrastructure as Code

Share this article