Fix Terraform "Invalid Index" Error

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.

text
Lists / tuples  →  numeric index [0], [1], …
Maps / objects  →  string key ["web"], .api

The 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/.

NOTE
Examples use the built-in 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:

bash
mkdir -p ~/terraform-labs/terraform-invalid-index-error/errors/missing-map-key
cd ~/terraform-labs/terraform-invalid-index-error/errors/missing-map-key

Reference a key that is not in the map:

bash
cat > main.tf <<'EOF'
terraform {
  required_version = ">= 1.12.0"
}

locals {
  apps = { web = "frontend", api = "backend" }
}

resource "terraform_data" "svc" {
  input = local.apps["db"]
}
EOF

Initialize and validate:

bash
terraform init -input=false
output
Terraform has been successfully initialized!

Run validate to surface the missing-key error:

bash
terraform validate -no-color
output
Error: 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:

bash
mkdir -p ~/terraform-labs/terraform-invalid-index-error/fixes/map-key
cd ~/terraform-labs/terraform-invalid-index-error/fixes/map-key

Use the existing api key instead:

bash
cat > main.tf <<'EOF'
terraform {
  required_version = ">= 1.12.0"
}

locals {
  apps = { web = "frontend", api = "backend" }
}

resource "terraform_data" "svc" {
  input = local.apps["api"]
}
EOF

Validate confirms the key matches the map:

bash
terraform init -input=false && terraform validate -no-color
output
Success! 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.

bash
mkdir -p ~/terraform-labs/terraform-invalid-index-error/errors/list-out-of-range
cd ~/terraform-labs/terraform-invalid-index-error/errors/list-out-of-range

Request index 2 from a two-element list:

bash
cat > main.tf <<'EOF'
terraform {
  required_version = ">= 1.12.0"
}

locals {
  tiers = ["web", "api"]
}

resource "terraform_data" "svc" {
  input = local.tiers[2]
}
EOF

Validate surfaces the length mismatch:

bash
terraform init -input=false && terraform validate -no-color
output
Error: 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:

bash
mkdir -p ~/terraform-labs/terraform-invalid-index-error/errors/empty-tuple/modules/echo
cd ~/terraform-labs/terraform-invalid-index-error/errors/empty-tuple

Write the child module:

bash
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
}
EOF

Reference the first ID unconditionally in the root module:

bash
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]
}
EOF

Validate passes because the expression type-checks, but plan fails when enabled is false:

bash
terraform init -input=false && terraform validate -no-color
output
Success! The configuration is valid.

Plan with enabled=false triggers the empty-tuple index error:

bash
terraform plan -no-color
output
Error: 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:

bash
mkdir -p ~/terraform-labs/terraform-invalid-index-error/fixes/empty-tuple/modules/echo
cd ~/terraform-labs/terraform-invalid-index-error/fixes/empty-tuple

Copy the same modules/echo/main.tf, then use a guarded consumer:

bash
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]
}
EOF

With enabled=false, plan reports no changes instead of an index error:

bash
terraform init -input=false && terraform plan -no-color -var="enabled=false"
output
No changes. Your infrastructure matches the configuration.

When enabled=true, apply creates both the module resource and the consumer:

bash
terraform apply -auto-approve -input=false -var="enabled=true"
output
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.

bash
mkdir -p ~/terraform-labs/terraform-invalid-index-error/errors/count-mismatch
cd ~/terraform-labs/terraform-invalid-index-error/errors/count-mismatch

Pair three names with only two ports:

bash
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]}"
}
EOF

Validate succeeds; plan fails on the third instance:

bash
terraform init -input=false && terraform plan -no-color
output
Error: 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:

hcl
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.

bash
mkdir -p ~/terraform-labs/terraform-invalid-index-error/errors/module-foreach-index/modules/app
cd ~/terraform-labs/terraform-invalid-index-error/errors/module-foreach-index

Child module:

bash
cat > modules/app/main.tf <<'EOF'
variable "name" {
  type = string
}

resource "terraform_data" "x" {
  input = var.name
}

output "id" {
  value = terraform_data.x.id
}
EOF

The root module incorrectly uses a numeric index on a module created with for_each:

bash
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
}
EOF

Plan shows the addressing mistake explicitly:

bash
terraform init -input=false && terraform plan -no-color
output
Error: 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:

hcl
module.app["web"].id

State addresses follow the same pattern:

text
module.app["web"].terraform_data.x
module.app["api"].terraform_data.x

Run 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.

bash
mkdir -p ~/terraform-labs/terraform-invalid-index-error/fixes/console-diagnostics
cd ~/terraform-labs/terraform-invalid-index-error/fixes/console-diagnostics

Define a local map for console inspection:

bash
cat > main.tf <<'EOF'
terraform {
  required_version = ">= 1.12.0"
}

locals {
  apps = { web = "frontend", api = "backend" }
}
EOF

Initialize, then query the local value:

bash
terraform init -input=false

List map keys (sorted alphabetically in console output):

bash
echo 'keys(local.apps)' | terraform console -no-color
output
[
  "api",
  "web",
]

Check size and type the same way:

bash
echo 'length(local.apps)' | terraform console -no-color
output
2

Print the structural type Terraform inferred:

bash
echo 'type(local.apps)' | terraform console -no-color
output
object({
    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:

bash
cd ~/terraform-labs/terraform-invalid-index-error/fixes/map-key
terraform validate -no-color && terraform plan -no-color
output
Success! The configuration is valid.

Plan: 1 to add, 0 to change, 0 to destroy.

Destroy lab resources when finished:

bash
cd ~/terraform-labs/terraform-invalid-index-error/fixes/empty-tuple && terraform destroy -auto-approve -input=false 2>/dev/null || true

References


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.


Frequently Asked Questions

1. What does Terraform Invalid index mean?

Terraform could not look up the index or key you requested because the collection does not contain that element. Common causes include a missing map key, a list index at or beyond length, an empty tuple from a conditional resource with count 0, or using a numeric index on a for_each module object.

2. What is the difference between Invalid index on a list and on a map?

Lists and tuples use numeric indexes starting at 0. Maps and objects use string keys. A list error often says the index is greater than or equal to the collection length. A map error says the given key does not identify an element in the collection value.

3. Why does Terraform say empty tuple?

A splat such as resource.example[*].id or a module output built from a counted resource returns an empty tuple when count is 0. Indexing that result with [0] fails because there is no first element.

4. Can I use try() to fix Invalid index?

try() can hide a data-shape bug by substituting a default when an index is missing. Prefer fixing the collection, aligning count with dependencies, or guarding with length() before you index. Reserve try() for optional arguments where null is genuinely acceptable.

5. How do I find valid map keys in Terraform?

Run terraform console in the working directory and evaluate keys(local.map_name) to list map keys, length() for size, and type() for the structural type. Match your expression to an actual key from that output.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)