Quick Answer
# Use try() for safe access
value = try(var.subnets[0], null)
value = try(var.tags["Environment"], "dev")
The Error
Error: Invalid index
on main.tf line 15:
15: subnet_id = aws_subnet.private[2].id
The given key does not identify an element in this collection value:
the collection has no element at index 2.
Or:
Error: Invalid index
on outputs.tf line 3:
3: value = local.config["missing_key"]
The given key does not identify an element in this collection value.
What Causes This
- List shorter than expected — accessing index 2 in a list of 2 items (0,1)
- Map key doesn’t exist — referencing a key that’s not in the map
- count/for_each produced fewer resources — conditional created 0 items
- Data source returned empty results — no matching resources found
- Variable default is empty —
default = []ordefault = {}
Solution 1: try() — The Best Fix
try() returns the first expression that doesn’t error:
# Safe list access
locals {
first_subnet = try(aws_subnet.private[0].id, null)
second_az = try(data.aws_availability_zones.available.names[1], "us-east-1b")
}
# Safe map access
locals {
env = try(var.config[terraform.workspace], var.config["dev"])
}
# Safe nested access
locals {
db_port = try(var.services.database.port, 5432)
}
Solution 2: lookup() for Maps
# lookup(map, key, default)
locals {
instance_type = lookup(var.instance_types, terraform.workspace, "t3.micro")
# Equivalent to:
# var.instance_types["prod"] → "t3.large" (if exists)
# var.instance_types["test"] → "t3.micro" (default)
}
variable "instance_types" {
default = {
dev = "t3.micro"
prod = "t3.large"
}
}
Solution 3: length() Check
# Check before accessing
output "first_subnet" {
value = length(aws_subnet.private) > 0 ? aws_subnet.private[0].id : null
}
# Conditional resource based on list
resource "aws_route_table_association" "private" {
count = length(aws_subnet.private)
subnet_id = aws_subnet.private[count.index].id
# ...
}
Solution 4: one() for Single Elements
# one() returns the single element or null if empty
data "aws_vpc" "selected" {
filter {
name = "tag:Environment"
values = ["prod"]
}
}
locals {
vpc_id = one(data.aws_vpc.selected[*].id)
}
Solution 5: coalesce() and coalescelist()
# First non-null value
locals {
region = coalesce(var.override_region, var.default_region, "us-east-1")
}
# First non-empty list
locals {
subnets = coalescelist(var.custom_subnets, data.aws_subnets.default.ids)
}
Common Patterns
Safe Data Source Access
data "aws_instances" "web" {
filter {
name = "tag:Role"
values = ["web"]
}
}
# Don't do this — crashes if no instances
output "first_ip" {
value = data.aws_instances.web.private_ips[0] # ❌
}
# Do this instead
output "first_ip" {
value = try(data.aws_instances.web.private_ips[0], "none") # ✅
}
Conditional count Access
resource "aws_eip" "nat" {
count = var.enable_nat ? 1 : 0
}
# Don't do this
output "nat_ip" {
value = aws_eip.nat[0].public_ip # ❌ Crashes if enable_nat = false
}
# Do this
output "nat_ip" {
value = try(aws_eip.nat[0].public_ip, null) # ✅
}
# Or this
output "nat_ip" {
value = var.enable_nat ? aws_eip.nat[0].public_ip : null # ✅
}
for_each Map Access
resource "aws_instance" "app" {
for_each = var.instances
# ...
}
# Access specific instance safely
output "web_ip" {
value = try(aws_instance.app["web"].private_ip, null)
}
Function Reference
| Function | Use Case |
|---|---|
try(expr, default) | Safe access to any expression |
lookup(map, key, default) | Safe map key lookup |
one(list) | Single element or null |
coalesce(a, b, c) | First non-null value |
coalescelist(a, b) | First non-empty list |
length(list) | Check list size before access |
contains(list, value) | Check if value exists |
can(expr) | Returns true if expression succeeds |
Hands-On Courses
- Terraform for Beginners on CopyPasteLearn
- Terraform By Example — practical code examples
Conclusion
Use try() as your default safe-access pattern — it works for lists, maps, and nested expressions. Use lookup() for simple map defaults, length() to guard list access, and one() for single-element data sources. Never access list indexes or map keys directly unless you’re certain they exist.
