Terraform Error: 'Duplicate object key' in a for expression collision
Fix Terraform's 'Duplicate object key' error in a for expression: two iterations produce the same key. Diagnose the collision and build unique keys for maps, for_each, and objects.
- #terraform
- #iac
- #troubleshooting
- #errors
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Exact Error Message
╷
│ Error: Duplicate object key
│
│ on main.tf line 11, in locals:
│ 11: by_name = { for u in var.users : u.name => u.email }
│ ├────────────────
│ │ u.name is "alice"
│
│ Two different items produced the key "alice" in this 'for' expression.
│ If duplicates are expected, use the ellipsis (...) after the value
│ expression to enable grouping by key.
╵
The named key and value vary with your data, but the message always points at the for expression producing the collision.
What It Means
An object-building for expression — { for x in coll : KEY => VALUE } — must produce a unique key on every iteration, because a Terraform map (object) cannot hold two entries with the same key. When two elements of the input collection generate the same key, Terraform cannot decide which value wins, so it fails at evaluation time rather than silently dropping one.
Terraform helpfully tells you the exact colliding key. The fix is either to choose a genuinely unique key expression or, if duplicates are legitimate, to use the grouping form (...) that collects all matching values into a list.
Common Causes
- The chosen key is not unique across the collection (two users with the same
name, two subnets in the same AZ). - Building a
for_eachmap keyed by a non-unique attribute, so distinct resources collapse to one key. - Case or whitespace differences masking apparent uniqueness (
"Alice"vs"alice"are different, butlower()makes them collide). - Deriving keys from a computed value that repeats (e.g. an instance type used by many resources).
- Flattening nested structures where the outer identifier is dropped, losing what made keys unique.
- Genuinely wanting to group values by a shared key but omitting the
...grouping mode.
Diagnostic Commands
Evaluate the expression in the console to see the collision without running a plan:
echo '{ for u in var.users : u.name => u.email }' | terraform console
List the raw key values so you can spot the duplicates directly:
echo '[for u in var.users : u.name]' | terraform console
Count occurrences of each candidate key to confirm which one repeats:
echo '{ for k, v in { for u in var.users : u.name => 1... } : k => length(v) }' | terraform console
Validate the whole configuration to surface the line and offending key:
terraform validate
Step-by-Step Resolution
-
Read the error — it names the duplicated key (
"alice"above). Decide whether that key should be unique or whether duplicates are expected. -
If the key should be unique, pick a genuinely unique attribute or compose one. For example key by an id, or combine two fields:
locals {
by_id = { for u in var.users : u.id => u.email }
# Or compose a compound key when no single field is unique:
by_name_dept = { for u in var.users : "${u.dept}-${u.name}" => u.email }
}
- If duplicates are legitimate and you want every value, switch to the grouping form by adding
...after the value expression. This produces a map of lists:
locals {
emails_by_name = { for u in var.users : u.name => u.email... }
# => { "alice" = ["alice@a.com", "alice@b.com"] }
}
- For a
for_eachmap, ensure the key is stable and unique per resource — never keyfor_eachby an index or a repeating attribute, or Terraform will merge or churn resources:
resource "aws_iam_user" "this" {
for_each = { for u in var.users : u.id => u }
name = each.value.name
}
- When flattening nested data, preserve the outer identifier in the key so uniqueness survives the flatten:
locals {
rules = { for r in flatten([
for sg_name, sg in var.groups : [
for i, rule in sg.rules : {
key = "${sg_name}-${i}"
rule = rule
}
]
]) : r.key => r.rule }
}
- Re-validate and confirm the collision is gone:
terraform validate && terraform plan
Prevention
- Prefer immutable, guaranteed-unique identifiers (ids, ARNs) as keys over human names.
- Compose compound keys (
"${a}-${b}") when no single field is unique. - When flattening nested loops, always fold an outer index or name into the key.
- Use the
...grouping form intentionally when many values share a key — do not reach for it just to silence the error. - Test map-building expressions in
terraform consoleagainst representative data before applying. - Keep
for_eachkeys stable across plans so resources are not destroyed and recreated on key changes.
Related Errors
Invalid for_each argument—for_eachgiven a value not known until apply or not a map/set of strings.Duplicate variable declaration— a different duplicate, twovariableblocks sharing a name.Invalid index— indexing a map with a key that does not exist.Iteration over null value— the collection passed toforis null rather than an empty list.
Frequently Asked Questions
What does the ... after the value do? It switches the for expression into grouping mode, collecting all values that share a key into a list. Use it when duplicates are expected.
Why does my for_each map complain about duplicate keys? You keyed it by a non-unique attribute, so two distinct items collapse to one key. Key by a unique id or a compound string instead.
How do I make a unique key when no single field is unique? Concatenate fields into a compound key, for example "${u.dept}-${u.name}", or fold an index into it when flattening.
Can lowercasing keys cause this? Yes — applying lower() can turn distinct keys like "Alice" and "alice" into the same key. Normalize deliberately and pick a field that stays unique. For more patterns, see the prompt library.
How do I confirm which key repeats? Evaluate [for x in coll : x.key] in terraform console and look for the duplicate value. More fixes are in the Terraform guides.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.