How to fix Error: Each object must have an attribute named content in Terraform

TerraformBEGINNERMEDIUMUpdated Dec 30, 2025ยท 118 views

Terraform dynamic blocks require a 'content' block to define the attributes of generated nested blocks. This error occurs when the content block is missing, incorrectly named, or when you try to pass data directly without the required content wrapper.

Quick fix

Fixes Error: Each object must have an attribute named content

dynamic "block_name" {
  for_each = var.items
  content {
    # attributes go here
    key = block_name.value.some_attribute
  }
}
  1. Understand the dynamic block structure
  2. Check if your dynamic block has a content block
  3. Verify the content block spelling
  4. Use .value to access iterator attributes
terraform โ€” error
dynamic "block_name" {
for_each = var.items
content {
# attributes go here
key = block_name.value.some_attribute
}
}
Error: Each object must have an attribute named content

What this error means

Terraform dynamic blocks are a way to generate repeated nested blocks programmatically using a loop. The dynamic block syntax requires three key parts: the dynamic label, a for_each iterator, and a content block that defines the attributes. When Terraform parses your configuration, it expects each dynamic block to contain a 'content' block that specifies what nested block attributes should be generated. This error means Terraform couldn't find the required content block in your dynamic block declaration.

Typical symptoms

  • Terraform returns 'Each object must have an attribute named content' during plan or apply
  • Error points to a dynamic block in your configuration file
  • The error message may include line numbers where the dynamic block is defined
  • Similar errors appear for other dynamic blocks in the same file
  • Error occurs even though the dynamic block looks correct at first glance

Common causes

  1. The 'content' block is completely missing from the dynamic block declaration
  2. The content block is misspelled (e.g., 'contents' or 'body' instead of 'content')
  3. Attempting to pass a variable or data structure directly to the dynamic block without wrapping it in a content block
  4. Incorrect indentation causing Terraform to not recognize the content block as part of the dynamic block
  5. Using a different block name instead of 'content' (e.g., 'config', 'attributes')

How to fix "Error: Each object must have an attribute named content"

1Understand the dynamic block structure

First, review the correct syntax for dynamic blocks in Terraform. A dynamic block must have this structure:

hcl
dynamic "block_name" {
  for_each = var.items
  content {
    # attributes go here
    key = block_name.value.some_attribute
  }
}

Note the three essential parts:
1. dynamic "block_name" - declares a dynamic block for a specific nested block type
2. for_each - provides the collection to iterate over
3. content - required block that defines what attributes each generated block should have

2Check if your dynamic block has a content block

Open your Terraform configuration file and locate the dynamic block mentioned in the error. Verify it contains a content block:

hcl
# WRONG - missing content block
dynamic "ingress" {
  for_each = var.ingress_rules
  {
    from_port = ingress.value.from_port
  }
}

# CORRECT - with content block
dynamic "ingress" {
  for_each = var.ingress_rules
  content {
    from_port = ingress.value.from_port
  }
}
3Verify the content block spelling

Ensure the keyword is spelled exactly as content (not contents, config, block, or any other variation):

hcl
# WRONG - misspelled
dynamic "tag" {
  for_each = var.tags
  contents {  # <-- Wrong spelling
    key   = tag.value.key
    value = tag.value.value
  }
}

# CORRECT - proper spelling
dynamic "tag" {
  for_each = var.tags
  content {  # <-- Correct spelling
    key   = tag.value.key
    value = tag.value.value
  }
}
4Use .value to access iterator attributes

Inside the content block, you must use iterator_name.value to access attributes from each item in the for_each collection:

hcl
# WRONG - missing .value
dynamic "security_group_rule" {
  for_each = var.rules
  content {
    type      = rules.protocol  # Missing .value
    from_port = rules.from_port # Missing .value
  }
}

# CORRECT - using .value
dynamic "security_group_rule" {
  for_each = var.rules
  content {
    type      = security_group_rule.value.protocol
    from_port = security_group_rule.value.from_port
  }
}

Note: The iterator name defaults to the block name (e.g., security_group_rule in this case), but you can customize it with the optional iterator argument.

5Check indentation and block nesting

Ensure proper HCL indentation so Terraform recognizes the content block as part of the dynamic block:

hcl
# WRONG - content block not properly nested
dynamic "listener" {
  for_each = var.listeners
ontent {  # <-- Missing indent makes this appear as new block
    port     = listener.value.port
  }
}

# CORRECT - content block properly indented
dynamic "listener" {
  for_each = var.listeners
  content {
    port     = listener.value.port
  }
}
6Validate the configuration

After making corrections, validate your Terraform configuration:

bash
terraform validate

If validation passes, the error should be resolved. If you still see the error, review the error message line number and re-examine the dynamic block at that location.

7Use optional iterator name for clarity (advanced)

For nested dynamic blocks or when you want clearer code, use the iterator argument to rename the loop variable:

hcl
dynamic "ingress" {
  for_each = var.ingress_rules
  iterator = rule  # Rename 'ingress' to 'rule' for clarity
  content {
    from_port = rule.value.from_port
    to_port   = rule.value.to_port
  }
}

This is especially helpful when you have nested dynamic blocks with the same name, as it removes ambiguity about which iterator you're referencing.

Advanced notes

Attributes as Blocks Mode: Some Terraform providers use a special 'attributes as blocks' mode for backward compatibility. In this mode, certain attributes can be written as nested blocks instead of key-value pairs. Dynamic blocks do NOT work with attributes in this mode because the configuration schema becomes ambiguous. If you encounter this issue, you must write those blocks statically instead of using dynamic blocks.

Nested Dynamic Blocks: When nesting dynamic blocks, each level needs its own content block. The iterator from an outer dynamic block is not automatically available in an inner dynamic block unless you explicitly pass it through the content block:

hcl
dynamic "outer" {
  for_each = var.outer_items
  content {
    dynamic "inner" {
      for_each = outer.value.inner_items
      content {
        value = inner.value.name
      }
    }
  }
}

Performance Consideration: While dynamic blocks are powerful for DRY configurations, overusing them can make your configuration harder to read and maintain. HashiCorp recommends using them primarily in reusable modules where they help create a clean user interface, rather than in root modules.

Frequently asked questions

What does "Error: Each object must have an attribute named content" mean?
Terraform dynamic blocks are a way to generate repeated nested blocks programmatically using a loop. The dynamic block syntax requires three key parts: the dynamic label, a for_each iterator, and a content block that defines the attributes. When Terraform parses your configuration, it expects each dynamic block to contain a 'content' block that specifies what nested block attributes should be generated.
How do I fix "Error: Each object must have an attribute named content"?
Terraform dynamic blocks require a 'content' block to define the attributes of generated nested blocks. This error occurs when the content block is missing, incorrectly named, or when you try to pass data directly without the required content wrapper.
What causes "Error: Each object must have an attribute named content"?
The 'content' block is completely missing from the dynamic block declaration The content block is misspelled (e.g., 'contents' or 'body' instead of 'content') Attempting to pass a variable or data structure directly to the dynamic block without wrapping it in a content block
Is the "Error: Each object must have an attribute named content" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading