| Tested on | Ubuntu 26.04 LTS (Resolute Raccoon) |
|---|---|
| Package | terraform 1.15.8-1 |
| Applies to | Any host with Terraform installed |
| Lab environment | Single Ubuntu VM with Terraform — Terraform lab environment on Ubuntu |
| Privilege | Normal user; sudo only if Terraform is not installed yet |
| Scope | Troubleshooting Terraform Invalid index — missing map keys, list and tuple out-of-range indexes, empty tuple from conditional count, count.index mismatches across collections, for_each module addressing, terraform console diagnostics with keys, length, and type, and safer guard patterns. Does not cover full list and map tutorials or state recovery. |
| Related guides | Terraform data types Terraform count vs for_each terraform console Terraform expressions Invalid for_each argument fixes |
Invalid index means an expression asked for an element that is not in the collection. Terraform validates many of these during terraform validate; others appear only when terraform plan evaluates count, splat results, or module outputs.
Lists / tuples → numeric index [0], [1], …
Maps / objects → string key ["web"], .apiThe fix is almost always to correct the address or the data shape — not to delete state or patch over the symptom.
Each scenario below uses its own directory under ~/terraform-labs/terraform-invalid-index-error/.
terraform_data resource and small local modules. Run terraform init in each new directory before terraform validate or terraform plan.
What causes Terraform Invalid index?
Terraform indexes collections in two ways:
| Collection kind | Lookup syntax | Typical error hint |
|---|---|---|
| List or tuple | list[0], tuple[1] |
index greater than or equal to the length |
| Map or object | map["key"], object.attr |
given key does not identify an element |
Reproduce a missing map key first:
mkdir -p ~/terraform-labs/terraform-invalid-index-error/errors/missing-map-key
cd ~/terraform-labs/terraform-invalid-index-error/errors/missing-map-keyReference a key that is not in the map:
cat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
locals {
apps = { web = "frontend", api = "backend" }
}
resource "terraform_data" "svc" {
input = local.apps["db"]
}
EOFInitialize and validate:
terraform init -input=falseTerraform has been successfully initialized!Run validate to surface the missing-key error:
terraform validate -no-colorError: Invalid index
on main.tf line 6, in resource "terraform_data" "svc":
6: input = local.apps["db"]
├────────────────
│ local.apps is object with 2 attributes
The given key does not identify an element in this collection value.The diagnostic names the collection (object with 2 attributes) and the bad key ("db"). Your next step is to list the keys that actually exist — covered in the console section below.
Fix the given key does not identify an element
Map and object lookups fail when the key or attribute name is wrong, typoed, or absent from the value Terraform computed.
Correct the reference to a key that exists. In the lab fix directory:
mkdir -p ~/terraform-labs/terraform-invalid-index-error/fixes/map-key
cd ~/terraform-labs/terraform-invalid-index-error/fixes/map-keyUse the existing api key instead:
cat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
locals {
apps = { web = "frontend", api = "backend" }
}
resource "terraform_data" "svc" {
input = local.apps["api"]
}
EOFValidate confirms the key matches the map:
terraform init -input=false && terraform validate -no-colorSuccess! The configuration is valid.When keys are dynamic or come from another module, inspect them in terraform console before hard-coding an index — see Inspect the value before fixing the expression.
Fix list index out of range
Lists and tuples use zero-based numeric indexes. Index 2 on a two-element tuple is out of range.
mkdir -p ~/terraform-labs/terraform-invalid-index-error/errors/list-out-of-range
cd ~/terraform-labs/terraform-invalid-index-error/errors/list-out-of-rangeRequest index 2 from a two-element list:
cat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
locals {
tiers = ["web", "api"]
}
resource "terraform_data" "svc" {
input = local.tiers[2]
}
EOFValidate surfaces the length mismatch:
terraform init -input=false && terraform validate -no-colorError: Invalid index
on main.tf line 6, in resource "terraform_data" "svc":
6: input = local.tiers[2]
├────────────────
│ local.tiers is tuple with 2 elements
The given key does not identify an element in this collection value: the
given index is greater than or equal to the length of the collection.Fix the index, shorten the list, or stop indexing entirely by converting to a map with stable keys — the pattern in Terraform count vs for_each avoids positional drift when list membership changes.
Fix collection has no elements (empty tuple)
An empty tuple often comes from a splat on a conditional resource where count = 0. The splat is valid syntax; indexing [0] on the result is not.
Create a small module that can return zero instances:
mkdir -p ~/terraform-labs/terraform-invalid-index-error/errors/empty-tuple/modules/echo
cd ~/terraform-labs/terraform-invalid-index-error/errors/empty-tupleWrite the child module:
cat > modules/echo/main.tf <<'EOF'
variable "enabled" {
type = bool
}
resource "terraform_data" "x" {
count = var.enabled ? 1 : 0
input = "on"
}
output "ids" {
value = terraform_data.x[*].id
}
EOFReference the first ID unconditionally in the root module:
cat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
variable "enabled" {
type = bool
default = false
}
module "echo" {
source = "./modules/echo"
enabled = var.enabled
}
resource "terraform_data" "consumer" {
input = module.echo.ids[0]
}
EOFValidate passes because the expression type-checks, but plan fails when enabled is false:
terraform init -input=false && terraform validate -no-colorSuccess! The configuration is valid.Plan with enabled=false triggers the empty-tuple index error:
terraform plan -no-colorError: Invalid index
on main.tf line 8, in resource "terraform_data" "consumer":
8: input = module.echo.ids[0]
├────────────────
│ module.echo.ids is empty tuple
The given key does not identify an element in this collection value: the
collection has no elements.Safer pattern — guard before indexing
Match the consumer's count to whether the splat has elements:
mkdir -p ~/terraform-labs/terraform-invalid-index-error/fixes/empty-tuple/modules/echo
cd ~/terraform-labs/terraform-invalid-index-error/fixes/empty-tupleCopy the same modules/echo/main.tf, then use a guarded consumer:
cat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
variable "enabled" {
type = bool
default = false
}
module "echo" {
source = "./modules/echo"
enabled = var.enabled
}
resource "terraform_data" "consumer" {
count = length(module.echo.ids) > 0 ? 1 : 0
input = module.echo.ids[0]
}
EOFWith enabled=false, plan reports no changes instead of an index error:
terraform init -input=false && terraform plan -no-color -var="enabled=false"No changes. Your infrastructure matches the configuration.When enabled=true, apply creates both the module resource and the consumer:
terraform apply -auto-approve -input=false -var="enabled=true"Apply complete! Resources: 2 added, 0 changed, 0 destroyed.If the desired result is an optional nullable value rather than an optional resource, one(module.echo.ids) is cleaner than indexing [0] — see Terraform expressions for one() and splat behavior. one() returns null for an empty collection and the element for a one-element collection. It is not equivalent to the guarded count pattern above: use count when the consumer itself should not exist, and one() when the consumer can exist with a null value.
Fix count.index beyond collection length
When count is driven by one list but expressions index into a shorter second list, the last instances fail.
mkdir -p ~/terraform-labs/terraform-invalid-index-error/errors/count-mismatch
cd ~/terraform-labs/terraform-invalid-index-error/errors/count-mismatchPair three names with only two ports:
cat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
locals {
names = ["a", "b", "c"]
ports = [80, 8080]
}
resource "terraform_data" "svc" {
count = length(local.names)
input = "${local.names[count.index]}:${local.ports[count.index]}"
}
EOFValidate succeeds; plan fails on the third instance:
terraform init -input=false && terraform plan -no-colorError: Invalid index
on main.tf line 8, in resource "terraform_data" "svc":
8: input = "${local.names[count.index]}:${local.ports[count.index]}"
├────────────────
│ count.index is 2
│ local.ports is tuple with 2 elements
The given key does not identify an element in this collection value: the
given index is greater than or equal to the length of the collection.Align the collections before indexing:
- Use
min(length(local.names), length(local.ports))only when intentionally truncating to the shorter collection is valid - If the collections are required to pair one-to-one, validate that their lengths match or, preferably, model each pair as one object so mismatched parallel lists cannot occur
- Prefer for_each with a keyed map when instance identity should survive collection edits
The object approach fixes the data shape so parallel lists cannot drift apart:
locals {
services = {
a = { port = 80 }
b = { port = 8080 }
}
}
resource "terraform_data" "svc" {
for_each = local.services
input = "${each.key}:${each.value.port}"
}Do not silence this with [0] on the wrong collection — that masks a real length mismatch.
Fix Invalid index with for_each or modules
for_each module instances are keyed by string, not by integer. module.app[0] on a for_each module is invalid.
mkdir -p ~/terraform-labs/terraform-invalid-index-error/errors/module-foreach-index/modules/app
cd ~/terraform-labs/terraform-invalid-index-error/errors/module-foreach-indexChild module:
cat > modules/app/main.tf <<'EOF'
variable "name" {
type = string
}
resource "terraform_data" "x" {
input = var.name
}
output "id" {
value = terraform_data.x.id
}
EOFThe root module incorrectly uses a numeric index on a module created with for_each:
cat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
module "app" {
for_each = toset(["web", "api"])
source = "./modules/app"
name = each.key
}
resource "terraform_data" "bad_ref" {
input = module.app[0].id
}
EOFPlan shows the addressing mistake explicitly:
terraform init -input=false && terraform plan -no-colorError: Invalid index
on main.tf line 8, in resource "terraform_data" "bad_ref":
8: input = module.app[0].id
├────────────────
│ module.app is object with 2 attributes
The given key does not identify an element in this collection value. An
object only supports looking up attributes by name, not by numeric index.Use the instance key:
module.app["web"].idState addresses follow the same pattern:
module.app["web"].terraform_data.x
module.app["api"].terraform_data.xRun terraform state list after apply to confirm keyed module paths. For module output wiring, see Terraform module inputs and outputs.
Inspect the value before fixing the expression
When the valid keys are not obvious, use terraform console before editing HCL.
mkdir -p ~/terraform-labs/terraform-invalid-index-error/fixes/console-diagnostics
cd ~/terraform-labs/terraform-invalid-index-error/fixes/console-diagnosticsDefine a local map for console inspection:
cat > main.tf <<'EOF'
terraform {
required_version = ">= 1.12.0"
}
locals {
apps = { web = "frontend", api = "backend" }
}
EOFInitialize, then query the local value:
terraform init -input=falseList map keys (sorted alphabetically in console output):
echo 'keys(local.apps)' | terraform console -no-color[
"api",
"web",
]Check size and type the same way:
echo 'length(local.apps)' | terraform console -no-color2Print the structural type Terraform inferred:
echo 'type(local.apps)' | terraform console -no-colorobject({
api: string,
web: string,
})A temporary output block can expose the same values in plan output when console is awkward in CI — remove debug outputs before merge.
Common bad fixes
These approaches hide symptoms without fixing the addressing problem:
| Bad fix | Why it fails long term |
|---|---|
Deleting terraform.tfstate |
State loss; does not fix the expression |
Removing .terraform.lock.hcl |
Unrelated to indexing; breaks provider pinning |
Wrapping every index in try() |
Masks missing data; defaults propagate silently |
Adding [0] everywhere |
Wrong element or empty tuple still breaks |
-target to skip the error |
Leaves configuration inconsistent |
Fix the collection length, map key, module address, or conditional count instead.
Verify the fix
After correcting the expression, run validate and plan in the fix directory:
cd ~/terraform-labs/terraform-invalid-index-error/fixes/map-key
terraform validate -no-color && terraform plan -no-colorSuccess! The configuration is valid.
Plan: 1 to add, 0 to change, 0 to destroy.Destroy lab resources when finished:
cd ~/terraform-labs/terraform-invalid-index-error/fixes/empty-tuple && terraform destroy -auto-approve -input=false 2>/dev/null || trueReferences
- Index operation — HashiCorp Developer
- Splat expressions — HashiCorp Developer
- Functions: one — HashiCorp Developer
Summary
Invalid index means Terraform looked up a list position or map key that does not exist. Map errors name the missing key; list errors report an index at or beyond length; empty tuple errors follow conditional count = 0 combined with [0] on a splat result; for_each modules require string keys such as module.app["web"], not module.app[0].
Use terraform console with keys(), length(), and type() to see the real collection before you change references. Align parallel lists or switch to maps and for_each when instance identity matters. Guard consumers with length() > 0 or matching count when the source collection can be empty — that fixes the data path instead of masking it.
For collection types and constraints, continue with Terraform data types. For keyed instances versus numeric indexes, see Terraform count vs for_each.

