This error occurs when you try to reference a list, tuple, or set value directly without specifying which element you want to access. Terraform requires an explicit index (like [0]) to access individual list elements.
Fixes Error: Cannot access value of type list without an index
# ❌ Wrong - accessing list without index
variable "servers" {
type = list(string)
default = ["web-1", "web-2", "web-3"]
}
output "server_name" {
value = var.servers # Error: Cannot access without index
}
# ✓ Correct - specify which element to access
output "server_name" {
value = var.servers[0] # Returns "web-1"
}# ❌ Wrong - accessing list without indexvariable "servers" {type = list(string)default = ["web-1", "web-2", "web-3"]}output "server_name" {value = var.servers # Error: Cannot access without index}# ✓ Correct - specify which element to accessoutput "server_name" {value = var.servers[0] # Returns "web-1"}Error: Cannot access value of type list without an index
In Terraform's HCL language, lists and tuples are ordered collections that must be accessed by index using bracket notation. When you reference a list variable or attribute without an index, Terraform cannot determine which element you want and throws this error. Additionally, sets (unordered collections) cannot be indexed at all - they must be converted to lists first using tolist() before accessing individual elements.
Use bracket notation with the element index (starting at 0) to access list values:
# ❌ Wrong - accessing list without index
variable "servers" {
type = list(string)
default = ["web-1", "web-2", "web-3"]
}
output "server_name" {
value = var.servers # Error: Cannot access without index
}
# ✓ Correct - specify which element to access
output "server_name" {
value = var.servers[0] # Returns "web-1"
}If accessing a set attribute, use tolist() to convert it to a list first:
# ❌ Wrong - trying to index a set
resource "aws_security_group" "example" {
# ...
}
output "first_tag" {
value = aws_security_group.example.tags[0] # Error: sets can't be indexed
}
# ✓ Correct - convert set to list then index
output "first_tag" {
value = tolist(aws_security_group.example.tags)[0]
}When you have multiple resources created with count and need to extract an attribute from all of them, use splat syntax:
# ❌ Wrong - trying to access list without index
resource "aws_instance" "servers" {
count = 3
# ...
}
output "all_ips" {
value = aws_instance.servers.private_ip # Error
}
# ✓ Correct - use splat syntax to get all values
output "all_ips" {
value = aws_instance.servers[*].private_ip # Returns ["10.0.1.1", "10.0.1.2", "10.0.1.3"]
}
# Or access a specific instance
output "first_ip" {
value = aws_instance.servers[0].private_ip
}When using data sources that return lists, verify the list isn't empty before accessing elements:
# ❌ Wrong - assuming list has elements
data "aws_subnets" "available" {
filter {
name = "vpc-id"
values = [aws_vpc.example.id]
}
}
output "subnet_id" {
value = data.aws_subnets.available.ids[0] # Fails if no subnets found
}
# ✓ Correct - verify with length() or use try()
output "subnet_id" {
value = length(data.aws_subnets.available.ids) > 0 ? data.aws_subnets.available.ids[0] : null
}
# Or use try() for graceful fallback
output "subnet_id" {
value = try(data.aws_subnets.available.ids[0], null)
}When passing list elements to resources that don't accept list types, use for_each to iterate:
variable "availability_zones" {
type = list(string)
default = ["us-east-1a", "us-east-1b", "us-east-1c"]
}
# ❌ Wrong - resource expects single AZ, not list
resource "aws_subnet" "example" {
availability_zone = var.availability_zones # Error
}
# ✓ Correct - use for_each to create one subnet per AZ
resource "aws_subnet" "example" {
for_each = toset(var.availability_zones)
availability_zone = each.value # References current AZ
# ...
}When using count to create multiple resources, use count.index to access the corresponding list element:
variable "instance_names" {
type = list(string)
default = ["web-1", "web-2", "web-3"]
}
# ❌ Wrong - passing entire list
resource "aws_instance" "servers" {
count = length(var.instance_names)
tags = {
Name = var.instance_names # Error: expects string, not list
}
}
# ✓ Correct - use count.index to access specific element
resource "aws_instance" "servers" {
count = length(var.instance_names)
tags = {
Name = var.instance_names[count.index] # Returns one name per instance
}
}Lists in Terraform are zero-indexed, meaning the first element is at index [0], the second at [1], etc. Sets are unordered collections that don't support indexing at all - always convert sets to lists with tolist() before accessing by index. The splat syntax [*] is particularly powerful for extracting attributes from multiple resources created with count. When working with dynamic blocks, remember that dynamic blocks create nested blocks within a resource, not new resources, so they follow different syntax rules. Use the element() function as an alternative way to access list items: element(var.my_list, 0) is equivalent to var.my_list[0].