Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for OpenTofu By James Joyner IV · · 8 min read Last reviewed Jul 2026

OpenTofu Error: 'Invalid for_each argument' unknown or wrong-typed value

Quick answer

Fix OpenTofu's 'Invalid for_each argument' error: handle values not known until apply and for_each on the wrong type by using maps or sets of strings and static keys.

  • #opentofu
  • #terraform
  • #iac
  • #troubleshooting
  • #errors
Free toolkit

Stuck on this OpenTofu error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Exact Error Message


│ Error: Invalid for_each argument

│   on main.tf line 8, in resource "aws_subnet" "this":
│    8:   for_each = toset(aws_instance.web[*].id)

│ The "for_each" map includes keys derived from resource attributes that
│ cannot be determined until apply, and so OpenTofu cannot determine the
│ full set of keys that will identify the instances of this resource.

A second common variant complains about type instead of unknown values:

│ The given "for_each" argument value is unsuitable: the "for_each" argument
│ must be a map, or set of strings, and you have provided a value of type list of string.

What It Means

for_each needs to know every instance key up front so it can build a stable address like aws_subnet.this["a"] for state tracking. OpenTofu enforces two hard rules: the keys must be known at plan time, and the value must be a map or a set of strings.

The first variant means your keys depend on an attribute (an ID, ARN, or computed value) that only exists after another resource is created. The second variant means you handed for_each a list, a set of non-strings, or some other unsuitable type. Both are configuration problems, caught during plan.

Common Causes

  • Deriving for_each keys from a not-yet-created resource’s computed attribute (.id, .arn).
  • Passing a list(string) directly instead of converting it with toset().
  • Using a set or map whose values — not just keys — are unknown until apply.
  • Feeding for_each the output of a data source that itself depends on an unapplied resource.
  • Mapping over objects where the intended key is a computed field rather than a static name.

Diagnostic Commands

Run plan to see the exact expression and which variant you hit:

tofu plan

Inspect the value you are passing to for_each in the console:

echo 'toset(var.subnet_names)' | tofu console

Check the type of the collection to confirm it is a map or set of strings:

tofu console
> type(var.subnet_names)

If keys come from another resource, check whether that resource is already in state:

tofu state list | grep aws_instance

Step-by-Step Resolution

  1. Read the error to decide which problem you have: unknown-until-apply keys, or a wrong type.

  2. If it is a type problem, convert a list to a set of strings:

resource "aws_subnet" "this" {
  for_each          = toset(var.subnet_names)   # set of strings
  vpc_id            = var.vpc_id
  availability_zone = each.value
  cidr_block        = var.cidr_by_az[each.value]
}
  1. If keys are unknown until apply, key on static, known values (names from a variable) rather than computed IDs:
variable "subnet_names" {
  type = set(string)
}

Then reference the created resources by that static key elsewhere:

route_table_id = aws_subnet.this["public-a"].id
  1. If you truly must derive from computed attributes, apply the dependency first with -target, then the dependent resource:
tofu apply -target=aws_instance.web
tofu apply
  1. When mapping objects, build the map so keys are static strings and only the values are computed:
for_each = { for s in var.subnets : s.name => s }
  1. Re-run plan to confirm the keys are now determinable:
tofu plan
Plan: 3 to add, 0 to change, 0 to destroy.

Prevention

  • Always key for_each on human-chosen, static values such as names or labels, never on .id/.arn outputs.
  • Wrap lists in toset() and objects in a { for ... } map comprehension before assigning to for_each.
  • Keep the keys and the values conceptually separate: values can be computed, keys cannot.
  • Prefer -target only as a temporary bootstrap, not as a permanent workaround for bad key design.
  • Validate collection types in the console before committing new for_each expressions.
  • Invalid index — the for_each succeeded but you indexed with a key that does not exist.
  • Invalid count argument — the count value itself is unknown until apply.
  • count.index and for_each are mutually exclusive — using both meta-arguments on one resource.
  • Reference to undeclared resource — the resource you derive keys from does not exist at all.

Frequently Asked Questions

Why can’t for_each keys come from a resource ID? Resource IDs are computed at apply time, but OpenTofu must know every instance key at plan time to build stable state addresses, so unknown keys are rejected.

What is the difference between the type variant and the unknown variant? The type variant means you passed something other than a map or set of strings; the unknown variant means the keys themselves are not determinable until apply.

Can I use a list with for_each? Not directly. Wrap it with toset() to convert it into a set of strings, or build a map keyed by a static field.

Is -target a real fix? It is a bootstrap workaround for genuine ordering needs, but the durable fix is to key on static values so no targeting is required. For reusable troubleshooting prompts, browse the prompt library, and for more fixes see the OpenTofu guides.

Free download · 368-page PDF

Fixed it? Get 500 OpenTofu & 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.

Did this fix your issue?

Free download · 368-page PDF

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.