opentofu/opentofu · error
cannot search an empty list
Error message
cannot search an empty list
What it means
Thrown by IndexFunc's Impl in internal/lang/funcs/collection.go. The first argument passed the list-or-tuple type check, is fully known, but LengthInt() == 0, so there is nothing to search. This 'easy path' short-circuits before element iteration.
Source
Thrown at internal/lang/funcs/collection.go:204
},
{
Name: "value",
Type: cty.DynamicPseudoType,
},
},
Type: function.StaticReturnType(cty.Number),
RefineResult: refineNotNull,
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
if !args[0].Type().IsListType() && !args[0].Type().IsTupleType() {
return cty.NilVal, errors.New("argument must be a list or tuple")
}
if !args[0].IsKnown() {
return cty.UnknownVal(cty.Number), nil
}
if args[0].LengthInt() == 0 { // Easy path
return cty.NilVal, errors.New("cannot search an empty list")
}
for it := args[0].ElementIterator(); it.Next(); {
i, v := it.Element()
eq, err := stdlib.Equal(v, args[1])
if err != nil {
return cty.NilVal, err
}
if !eq.IsKnown() {
return cty.UnknownVal(cty.Number), nil
}
if eq.True() {
return i, nil
}
}
return cty.NilVal, errors.New("item not found")
},View on GitHub (pinned to 3561785c48)
Solutions
- Guard the call: length(var.list) > 0 ? index(var.list, v) : -1 (or another sentinel you handle).
- Use contains() first if absence is normal: contains(var.list, v) ? index(var.list, v) : null.
- Fix the data source or variable so the list is genuinely populated before index() runs.
- Wrap with try(index(var.list, v), null) when emptiness is acceptable.
Example fix
# before idx = index(var.availability_zones, var.az) # after idx = length(var.availability_zones) > 0 ? index(var.availability_zones, var.az) : -1
Defensive patterns
Strategy: validation
Validate before calling
# Check for content before searching idx = length(var.list) > 0 ? index(var.list, v) : -1
Try / catch
try(index(var.list, v), -1) covers both the empty-list and not-found errors in one guard.
Prevention
- Guard optional lists with length() checks.
- Populate lists from validated data sources before positional search.
- Add preconditions when emptiness indicates an upstream bug.
When it happens
Trigger: index([], "x") literally, or index(var.list, v) where var.list evaluated to an empty list at plan/apply time. Unknown lists return unknown earlier, so only known empty collections reach this branch.
Common situations: A module receives an optional list that is empty in this deployment but index() is called unconditionally; a for_each/count data source produced zero items; a default empty list with no guard before positional search.
Related errors
- argument must be a list or tuple
- item not found
- argument must be a string, a collection type, or a structura
- all arguments must have the same type
- no non-null, non-empty-string arguments
AI-assisted analysis of opentofu/opentofu@3561785c48 (2026-08-15).
Data as JSON: /api/errors/7a6fec7ffec2a42c.
Report an issue: GitHub.