This error occurs when Terraform detects malformed JSON in your configuration, typically in JSON-encoded strings, heredocs, or when mixing HCL with inline JSON. JSON requires proper structure with quoted keys, commas, and no trailing commas.
The "Invalid JSON syntax" error indicates that Terraform encountered JSON data that does not conform to the JSON specification. This commonly occurs in several scenarios: when using Terraform's `jsonencode()` function with improperly structured data, when embedding JSON strings directly in HCL, when using `jsondecode()` to parse invalid JSON, or when JSON is embedded in heredocs or local values. Terraform is strict about JSON validation because JSON-encoded data is often used in policies, configurations, and data passed to cloud providers. A single malformed JSON string can cause the entire apply to fail. The error message usually indicates the line and character position where the parser encountered the problem, though sometimes this requires careful reading of the error output. Common culprits include trailing commas (valid in HCL but not JSON), unquoted keys or values, improper escaping of quotes, control characters in strings, and BOM (Byte Order Mark) characters at the start of files.
First, identify where the JSON is coming from. Check the Terraform error message for file location and line number:
Error: Invalid JSON syntax: ...
on main.tf line 42, in resource "aws_iam_role_policy" "example":
42: policy = jsonencode({The error usually points to where the jsonencode() call or JSON string starts. Look at that specific location in your configuration.
Trailing commas are allowed in HCL but forbidden in JSON. This is a very common cause:
Wrong - trailing comma in JSON:
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = "s3:GetObject", # This comma is fine in HCL
}, # But a trailing comma here is NOT allowed in JSON!
],
})Correct - no trailing comma:
policy = jsonencode({
Version = "2012-10-17",
Statement = [
{
Effect = "Allow",
Action = "s3:GetObject" # No comma after the last item
}
]
})This is the single most common cause of "Invalid JSON syntax" errors. Even though HCL allows trailing commas for convenience, jsonencode() produces strict JSON.
JSON requires all keys and string values to be enclosed in double quotes. HCL is more flexible:
Wrong - unquoted keys in JSON:
value = jsonencode({
name: "John", # Keys must be quoted in JSON
age: 30 # Bare numbers are fine, but keys aren't
})Correct - quoted keys:
value = jsonencode({
"name" = "John", # Keys are quoted (using = not :)
"age" = 30 # Numbers don't need quotes
})In HCL using jsonencode(), use this syntax:
- Use = for key-value pairs (not :)
- Quote keys and string values: "key" = "value"
- Don't quote numbers or booleans: "count" = 5, "active" = true
When JSON is embedded in HCL strings, quotes must be properly escaped. Use jsonencode() to avoid manual escaping:
Wrong - improper escaping:
# Dangerous and error-prone
policy = "{\"Version\": \"2012-10-17\", \"Statement\": [...]}"Correct - use jsonencode():
# Let Terraform handle the escaping
policy = jsonencode({
Version = "2012-10-17",
Statement = [...]
})If you must use raw JSON strings, use a heredoc:
policy = <<-EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*"
}
]
}
EOFThe heredoc preserves the literal JSON without HCL escaping requirements.
Use terraform console to test jsonencode() expressions in isolation:
# Start interactive console
terraform console
# Test your jsonencode expression
> jsonencode({
"name" = "test",
"value" = 123
})
"{"name":"test","value":123}"
# Test jsondecode to verify it's valid
> jsondecode(jsonencode({
"name" = "test"
}))
{
"name" = "test"
}If terraform console works but your configuration doesn't, look for differences in the surrounding context.
Exit the console by pressing Ctrl+C or Ctrl+D.
If you're loading JSON from external files, validate them separately:
# Validate JSON syntax using jq (if installed)
jq . policy.json
# Or using Python
python3 -m json.tool policy.json
# Using Node.js
node -e "console.log(JSON.parse(require('fs').readFileSync('policy.json')))"All of these tools will report exactly where the JSON is invalid. If they pass, the issue may be with how Terraform is reading the file.
For Terraform:
# Load and parse JSON from file
policy = file("${path.module}/policy.json")
# Or parse it if it's stored as a JSON string
parsed_data = jsondecode(file("${path.module}/data.json"))Sometimes JSON fails due to invisible characters like BOM, tabs, or Unicode characters:
# Check file encoding (should be UTF-8)
file policy.json
# Look for BOM at the start
hexdump -C policy.json | head -1
# BOM appears as: ef bb bf
# Check for tabs vs spaces
cat -A policy.json
# Tabs appear as ^I, spaces as regular spacesCommon issues:
- BOM (Byte Order Mark): Some editors add UTF-8 BOM. Delete it in your editor.
- Mixed tabs/spaces: JSON is sensitive to indentation. Use consistent spaces (not tabs).
- Control characters: Remove any non-printable characters.
- Smart quotes: Copy-pasting from word processors sometimes converts " to " or "
In your Terraform configuration, if JSON comes from a file, ensure it's saved as UTF-8 without BOM.
JSON has specific data types. Ensure your HCL data matches JSON expectations:
Valid JSON types:
jsonencode({
"string" = "text", # Strings (quoted)
"number" = 42, # Numbers (no quotes)
"float" = 3.14, # Floats (no quotes)
"boolean" = true, # Booleans: true/false (lowercase, no quotes)
"null" = null, # Null value
"array" = [1, 2, 3], # Arrays
"object" = { "key" = "val" } # Objects
})Invalid:
# These will cause issues
jsonencode({
"undefined" = undefined, # HCL feature, not valid in JSON
"single" = 'quoted', # JSON requires double quotes, not single
"unquoted" = unquoted_value # Must be quoted or be null/true/false/number
})If you need to handle null or optional values in HCL before encoding:
locals {
policy = jsonencode({
"optional_field" = var.some_field != null ? var.some_field : "default"
})
}If Terraform is giving you a generic "Invalid JSON syntax" error, wrap the JSON in jsondecode() to get a better error message:
Before - vague error:
policy = file("${path.module}/policy.json") # Might fail silentlyBetter - explicit validation:
# This will immediately fail if policy.json is invalid JSON
policy = jsonencode(jsondecode(file("${path.module}/policy.json")))jsondecode() provides more specific error messages that pinpoint the exact problem in your JSON file.
Once you've fixed the JSON, you can remove the outer jsonencode() wrapper:
policy = file("${path.module}/policy.json")HCL vs JSON syntax: Terraform accepts both HCL and JSON (.tf.json) files. HCL is more forgiving (allows unquoted keys, trailing commas, comments), while JSON is strict. When using jsonencode(), you're converting HCL data to strict JSON. This is why HCL syntax that works in .tf files causes errors when jsonencoded.
Large policy documents: For complex IAM or resource policies, store them in separate .json files rather than embedding them in HCL:
# main.tf
resource "aws_iam_role_policy" "example" {
role = aws_iam_role.example.name
policy = file("${path.module}/policies/policy.json")
}This approach is cleaner and allows you to validate JSON independently.
Template strings and interpolation: When using template strings with JSON, be careful with interpolation:
# Correct - interpolate inside jsonencode
policy = jsonencode({
"resource" = "arn:aws:s3:::${aws_s3_bucket.example.id}"
})
# Problematic - raw string with interpolation
policy = "{ \"resource\": \"arn:aws:s3:::${aws_s3_bucket.example.id}\" }"Terraform version considerations: Terraform 0.12+ handles JSON strictly. Older versions may have more lenient parsing. Always upgrade to a supported version.
Debugging output: Use terraform console or terraform output to inspect the actual JSON string being generated before Terraform fails on it:
terraform console
> base64encode(var.json_policy) # See exact bytesError: Error rendering template: template not found
How to fix "template not found" error in Terraform
Error: Error generating private key
How to fix 'Error generating private key' in Terraform
Error creating Kubernetes Service: field is immutable
How to fix "field is immutable" errors in Terraform
Error: Error creating local file: open: permission denied
How to fix "Error creating local file: permission denied" in Terraform
Error: line endings have changed from CRLF to LF
Line endings have changed from CRLF to LF in Terraform