`-target` makes a non-targeted module instance evaluate its resources as an empty tuple, failing the plan
OpenTofu Version
OpenTofu v1.12.6
on darwin_arm64This is not OpenTofu-specific — the same configuration produces the same error on current Terraform, so i'm guessing this behaviour is probably long-standing and inherited rather than introduced by OpenTofu:
| Binary | Result |
|---|---|
| OpenTofu 1.12.6 (darwin_arm64) | reproduces |
| OpenTofu 1.12.3 (linux_arm64) | reproduces |
| Terraform 1.16.2 | reproduces |
| Terraform 1.5.7 (last pre-fork release) | reproduces |
OpenTofu Configuration Files
Three files, no providers required.
main.tf:
locals {
agents = {
a = { adhoc = false }
b = { adhoc = true }
}
}
module "agent" {
source = "./child"
for_each = local.agents
adhoc = each.value.adhoc
}child/main.tf:
variable "adhoc" {
type = bool
}
resource "terraform_data" "sa" {
count = var.adhoc ? 1 : 0
}
module "vm" {
source = "./vm"
email = var.adhoc ? terraform_data.sa[0].id : null
}child/vm/main.tf:
variable "email" {
type = string
nullable = true
}
resource "terraform_data" "vm" {
input = var.email
}Debug Output
From tofu init followed by tofu plan -target='module.agent["a"]' on an empty
state, in the directory holding the three files above. The routine "Resource
targeting is in effect" warning is omitted; this is the tail of the output:
Error: Invalid index
on child/main.tf line 11, in module "vm":
11: email = var.adhoc ? terraform_data.sa[0].id : null
├────────────────
│ terraform_data.sa is empty tuple
The given key does not identify an element in this collection value: the
collection has no elements.Full TF_LOG=trace output for that run (~1000 lines) can be provided if useful.
Four lines from it carry the whole sequence, in this order:
[TRACE] ResourceCountTransformer: adding module.agent["b"].terraform_data.sa[0] as *tofu.NodePlannableResourceInstance
[DEBUG] Resource instance state not found for node "module.agent[\"b\"].terraform_data.sa[0]", instance module.agent["b"].terraform_data.sa[0]
[DEBUG] Removing "module.agent[\"b\"].terraform_data.sa[0]", filtered by targeting.
[TRACE] nodeModuleVariable: evaluating module.agent["b"].module.vm.var.emailcount evaluates to 1 and the instance node is created; it has no state because
it was never applied; TargetingTransformer then removes it; and afterwards the
child module's variable for that same pruned instance is still evaluated. The
resource is gone from the graph, but the expression referencing it is not.
Expected Behavior
tofu plan -target='module.agent["a"]' plans module.agent["a"] and says
nothing about module.agent["b"], which is excluded from the plan.
Within module.agent["b"] the configuration is internally consistent:
terraform_data.sa has count = 1 whenever var.adhoc is true, which is
exactly when the expression indexes [0]. There is no input for which that
index is out of range.
Actual Behavior
The plan fails with Invalid index on an expression belonging to
module.agent["b"] — the instance that was not targeted. terraform_data.sa
resolves to an empty tuple there, even though its count evaluates to 1.
Two conditions appear to be jointly required:
- The non-targeted instance's resource has no state entry yet (never applied).
- The reference sits in a module call argument. Module arguments are still evaluated for pruned instances, whereas the resources those arguments reference are not, so the reference falls back to (empty) state.
If the resource has already been applied, the reference resolves from state and the plan succeeds — so the failure depends on apply history of an instance that is not part of the plan.
Steps to Reproduce
tofu init
tofu plan -target='module.agent["a"]'on an empty state. Note the error is attributed to child/main.tf, with no
module instance address, which makes it read as a problem with the targeted
instance.
For contrast:
tofu plan(no-target) succeeds.tofu applyonce, thentofu plan -target='module.agent["a"]'succeeds, becauseterraform_data.sanow has state.- Setting
adhoc = falseonb(so itscountis 0 and the conditional short-circuits) also succeeds.
Additional Context
This is straightforward to hit in a root module that holds many instances of the
same module in one state, where routine per-instance work relies on -target: a
configuration change to one instance that has not been applied yet breaks
targeted plans for an unrelated instance, and the error names neither of them.
Suggested direction: a resource excluded from a targeted plan is not known to have zero instances — it is unknown. Evaluating it as an empty tuple turns "excluded from this plan" into a factual claim about the configuration, and that claim then propagates into expressions that were never part of the plan. Having such references evaluate as unknown would let downstream expressions degrade to unknown instead of erroring.
Workaround for anyone hitting this: avoid indexing in module arguments — use
one(resource.name[*].attr) instead of resource.name[0].attr. The value is
identical for the instance actually being planned, and the pruned instance
yields null, which nothing consumes.
References
- opentofu/opentofu#1639 (Tofu Graph Target Reduction) — targeting/graph-pruning design
- opentofu/opentofu#2934 — targeting +
for_eachproducing unexpected results - opentofu/opentofu#4227 —
-target/-excludein the new language runtime
Source: opentofu/opentofu