This error occurs when Terraform tries to access an element (using bracket notation) of a value that is null. It happens with data sources that return no results or when accessing resources created conditionally with count.
In Terraform, when you try to reference a specific index of a collection or object that doesn't exist or is null, the language throws an "Attempt to index null value" error. This commonly happens when: 1. A data source query returns no results (making the result null) 2. A resource created with count = 0 produces an empty collection 3. You reference an indexed attribute from a resource that may not exist 4. Server-side modifications create null values in expected fields The error message tells you that Terraform cannot evaluate an expression like `data.some_data.items[0].id` because `data.some_data.items` is null, and you cannot index into a null value.
The safest and most idiomatic Terraform solution is to use the try() function, which gracefully handles null values:
# Instead of this (fails if items is null):
value = data.aws_instances.example.ids[0]
# Do this (returns null if indexing fails):
value = try(data.aws_instances.example.ids[0], null)
# Or with a default:
instance_id = try(data.aws_instances.example.ids[0], "i-default123")The try() function attempts to evaluate the expression, and if it encounters an error (like indexing null), it returns the second argument (the default) instead. This is the recommended approach because it's clear and handles all edge cases.
Use a conditional expression to verify the collection has elements before accessing an index:
# Check if the list has at least one element
instance_id = length(data.aws_instances.example.ids) > 0 ? data.aws_instances.example.ids[0] : null
# For objects with optional attributes:
zone = length(data.aws_availability_zones.available.names) > 0 ? data.aws_availability_zones.available.names[0] : "us-west-2a"This approach explicitly checks that the collection is non-empty and has at least one item before accessing it. Use null if the value is optional, or a sensible default if the argument is required.
If you're indexing into a set (which doesn't support index-based access), convert it to a list first:
# Sets don't support indexing - this fails:
domain_record = aws_acm_certificate.main.domain_validation_options[0]
# Convert to list first:
domain_record = try(tolist(aws_acm_certificate.main.domain_validation_options)[0], null)
# Or use a conditional:
domain_record = length(aws_acm_certificate.main.domain_validation_options) > 0 ? tolist(aws_acm_certificate.main.domain_validation_options)[0] : nullThe tolist() function converts a set to a list, which can then be indexed with [0], [1], etc.
If the null value is due to timing issues, ensure proper dependencies are declared:
data "aws_organizations_organization" "main" {}
resource "aws_organizations_account" "member" {
name = "Member Account"
email = "[email protected]"
parent_id = try(data.aws_organizations_organization.main.roots[0].id, null)
depends_on = [data.aws_organizations_organization.main]
}The depends_on meta-argument ensures Terraform reads the data source before trying to use its values. This helps when the initial query might be null because the resource being queried isn't ready yet.
Use terraform console to interactively check what values are actually returned:
terraform console
# Check what the data source returns:
> data.aws_instances.example.ids
# Result shows the actual value (list, empty list, or null)
> length(data.aws_instances.example.ids)
# Shows the length, helping you understand why indexing fails
# Test your conditional:
> length(data.aws_instances.example.ids) > 0 ? data.aws_instances.example.ids[0] : "default"This interactive shell lets you evaluate expressions and see exactly what values are being returned, making it much easier to write the correct conditional or try() expression.
Version compatibility: The try() function was introduced in Terraform 0.14. If you're using an older version, use conditional expressions with length() checks instead.
Null coalescing: You can chain multiple try() or coalesce() calls to handle multiple fallback options:
primary = try(data.primary.ids[0], null)
fallback = try(data.secondary.ids[0], null)
value = coalesce(primary, fallback, "default-value")Dynamic blocks and count: When using dynamic blocks or count to conditionally create resources, always use try(resource.name[0].id, null) or length(resource.name) > 0 ? resource.name[0].id : null when referencing the result in other parts of your configuration.
Performance tip: Place null-checking conditions early in your expressions. Terraform short-circuits conditional evaluation, so length(...) > 0 ? ...[0] : ... is more efficient than wrapping the entire expression in try().
Error: Error installing helm release: cannot re-use a name that is still in use
How to fix "release name in use" error in Terraform with Helm
Error: Error creating GKE Cluster: BadRequest
BadRequest error creating GKE cluster in Terraform
Error: External program failed to produce valid JSON
External program failed to produce valid JSON
Error: Unsupported argument in child module call
How to fix "Unsupported argument in child module call" in Terraform
Error: network is unreachable
How to fix "network is unreachable" in Terraform