hashicorp/terraform · error

argument must be a list or tuple

Error message

argument must be a list or tuple

What it means

Thrown by IndexFunc.Impl (the HCL index() builtin) when the first argument's type is neither a list nor a tuple. index() only searches ordered list/tuple collections for a value.

Source

Thrown at internal/lang/funcs/collection.go:193

})

// IndexFunc constructs a function that finds the element index for a given value in a list.
var IndexFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name: "list",
			Type: cty.DynamicPseudoType,
		},
		{
			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

View on GitHub (pinned to c9def3e214)

Solutions

  1. Use lookup() or direct .attr / map[key] access for maps/objects.
  2. Convert sets to lists: index(tolist(var.set), value).
  3. Use strindex/regex for substring position in a string.

Example fix

# before
locals { i = index(var.tags_set, "prod") }   # set -> error
# after
locals { i = index(tolist(var.tags_set), "prod") }
# for a map, use lookup instead:
locals { v = lookup(var.colors, "red", "none") }
Defensive patterns

Strategy: type-guard

Validate before calling

# ensure first arg is a list/tuple before index()
locals {
  ok  = can(index(tolist(var.x), "y"))
  res = local.ok ? index(tolist(var.x), "y") : -1
}

Type guard

# normalize sets to lists; redirect maps to lookup()
locals {
  i = length(var.x) >= 0 ? index(tolist(var.x), "y") : -1
  v = lookup(var.m, "k", "none")
}

Try / catch

locals { i = try(index(tolist(var.x), "y"), -1) }

Prevention

When it happens

Trigger: index("abc", "b") (a string), index({a=1}, "a") (a map/object), index(toset([...]), x) (a set).

Common situations: Confusing index() with map lookup; passing a set (unordered, so index is meaningless); assuming index() works on strings like Python.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/65449b70a249ae3c. Report an issue: GitHub.