{"record":{"id":"7a6fec7ffec2a42c","repo":"opentofu/opentofu","slug":"cannot-search-an-empty-list","errorCode":null,"errorMessage":"cannot search an empty list","messagePattern":"cannot search an empty list","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/lang/funcs/collection.go","lineNumber":204,"sourceCode":"\t\t},\n\t\t{\n\t\t\tName: \"value\",\n\t\t\tType: cty.DynamicPseudoType,\n\t\t},\n\t},\n\tType:         function.StaticReturnType(cty.Number),\n\tRefineResult: refineNotNull,\n\tImpl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {\n\t\tif !args[0].Type().IsListType() && !args[0].Type().IsTupleType() {\n\t\t\treturn cty.NilVal, errors.New(\"argument must be a list or tuple\")\n\t\t}\n\n\t\tif !args[0].IsKnown() {\n\t\t\treturn cty.UnknownVal(cty.Number), nil\n\t\t}\n\n\t\tif args[0].LengthInt() == 0 { // Easy path\n\t\t\treturn cty.NilVal, errors.New(\"cannot search an empty list\")\n\t\t}\n\n\t\tfor it := args[0].ElementIterator(); it.Next(); {\n\t\t\ti, v := it.Element()\n\t\t\teq, err := stdlib.Equal(v, args[1])\n\t\t\tif err != nil {\n\t\t\t\treturn cty.NilVal, err\n\t\t\t}\n\t\t\tif !eq.IsKnown() {\n\t\t\t\treturn cty.UnknownVal(cty.Number), nil\n\t\t\t}\n\t\t\tif eq.True() {\n\t\t\t\treturn i, nil\n\t\t\t}\n\t\t}\n\t\treturn cty.NilVal, errors.New(\"item not found\")\n\n\t},","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/opentofu/opentofu/blob/3561785c48c1ce615e7c50261bd351f26053efa2/internal/lang/funcs/collection.go#L186-L222","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nidx = index(var.availability_zones, var.az)\n\n# after\nidx = length(var.availability_zones) > 0 ? index(var.availability_zones, var.az) : -1","handlingStrategy":"validation","validationCode":"# Check for content before searching\nidx = length(var.list) > 0 ? index(var.list, v) : -1","typeGuard":null,"tryCatchPattern":"try(index(var.list, v), -1) covers both the empty-list and not-found errors in one guard.","preventionTips":["Guard optional lists with length() checks.","Populate lists from validated data sources before positional search.","Add preconditions when emptiness indicates an upstream bug."],"tags":["hcl","functions","index","empty-collection"],"backgroundTag":null,"analyzedSha":"3561785c48c1ce615e7c50261bd351f26053efa2","analyzedAt":"2026-08-15T23:27:16.226Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}