The 'Invalid index' error occurs when Terraform tries to access an element in a list, tuple, or map using an index or key that doesn't exist. This commonly happens with empty collections, out-of-bounds array access, or missing map keys. The fix involves checking collection bounds, using conditional expressions, or applying the try() function.
Terraform's configuration language allows you to access elements in lists, tuples, and maps using bracket notation (e.g., var.subnets[0] or var.tags["key"]). When Terraform evaluates your configuration, it validates that the index or key you're referencing actually exists in the collection. If it doesn't—either because the collection is empty, the index is too large, or the key doesn't exist—Terraform raises an 'Invalid index' error. This prevents your configuration from being parsed and applied.
Review the full error message from terraform plan or apply. The error will show:
Error: Invalid index
on main.tf line X, in resource "aws_instance" "example":
X: availability_zone = var.availability_zones[0]
The given key does not identify an element in this collection value: the collection has no elements.Note the exact file, line, and variable/reference being accessed (in this example: var.availability_zones[0]).
Verify whether the collection you're accessing has any elements. If you're referencing a variable, check its default value or how it's being set:
variable "availability_zones" {
type = list(string)
default = [] # This is empty!
}
# This will fail:
resource "aws_instance" "example" {
availability_zone = var.availability_zones[0]
}If the list is intentionally empty or may be empty, you need to handle that case.
The try() function attempts to evaluate an expression and returns a default value if it fails. This is the simplest fix for potential invalid index errors:
# Instead of:
availability_zone = var.availability_zones[0]
# Use:
availability_zone = try(var.availability_zones[0], "us-east-1a")Or return null if no default is appropriate:
availability_zone = try(var.availability_zones[0], null)The try() function will evaluate the expression safely and return the fallback value if the index is invalid.
For more explicit control, use the length() function to verify the collection has elements before accessing by index:
resource "aws_instance" "example" {
# Only set if the list has at least one element
availability_zone = length(var.availability_zones) > 0 ? var.availability_zones[0] : "us-east-1a"
}Or use it in a conditional to skip the resource entirely:
resource "aws_instance" "example" {
count = length(var.availability_zones) > 0 ? 1 : 0
availability_zone = var.availability_zones[0]
}When accessing map keys that might not exist, use lookup() to provide a default:
# Instead of:
environment = var.tags["environment"]
# Use:
environment = lookup(var.tags, "environment", "development")Or use the conditional operator with contains() to check first:
environment = contains(keys(var.tags), "environment") ? var.tags["environment"] : "development"If using count or for_each, ensure your reference matches the iteration mode:
# With count:
resource "aws_instance" "example" {
count = var.instance_count
# ...
}
# Later, reference by index (0-based):
resource "aws_security_group_rule" "example" {
security_group_id = aws_instance.example[0].security_group_id # Correct
}
# With for_each:
resource "aws_instance" "example" {
for_each = var.instances # Map or set
# ...
}
# Reference by key:
resource "aws_security_group_rule" "example" {
security_group_id = aws_instance.example["web"].security_group_id # Correct
}Ensure the key or index you're using actually exists in the iteration.
Add validation to your variables to prevent empty collections from being passed in:
variable "availability_zones" {
type = list(string)
validation {
condition = length(var.availability_zones) > 0
error_message = "availability_zones must not be empty."
}
}This catches the problem early with a clear error message before Terraform tries to evaluate your resources.
After making changes, run a plan to verify the error is resolved:
terraform planIf you see a different error or the same error on a different line, repeat steps 1-3 for the next invalid reference. Once plan succeeds, you can proceed with:
terraform applyModule Outputs: When referencing outputs from modules, especially those created conditionally (with count or for_each), the output may be an empty tuple. For example, module.vpc[0].subnet_ids will fail if count.index is out of bounds. Use try(module.vpc[0].subnet_ids[0], null) or check if the module was created: length(module.vpc) > 0 ? module.vpc[0].subnet_ids[0] : null.
Dynamic Blocks: When using dynamic blocks with variable-length lists, ensure the list isn't empty. If iteration count is zero, the dynamic block won't create any nested blocks—other parts of your config should account for this.
Splat Syntax: The splat syntax aws_instance.example[*].id safely returns an empty list if count is 0, whereas aws_instance.example[0].id will error. Use splat syntax when you're unsure of collection size.
Debugging: Enable debug logging to see how Terraform evaluates expressions: TF_LOG=DEBUG terraform plan. This helps identify when collections become empty during evaluation.
Error: Error rendering template: template not found
How to fix "template not found" error in Terraform
Error: Error generating private key
How to fix 'Error generating private key' in Terraform
Error creating Kubernetes Service: field is immutable
How to fix "field is immutable" errors in Terraform
Error: Error creating local file: open: permission denied
How to fix "Error creating local file: permission denied" in Terraform
Error: line endings have changed from CRLF to LF
Line endings have changed from CRLF to LF in Terraform