hashicorp/terraform · error

argument must be a list or tuple

Error message

argument must be a list or tuple

What it means

Thrown by the `index()` function (IndexFunc) in its Impl when the first argument's type is neither a list nor a tuple. index() searches an ordered sequence for a value and returns its numeric position, so non-sequence inputs (maps, sets, strings, scalars) are invalid. The check runs before any unknown-value handling.

Solutions

  1. Ensure the first argument is a list(...) or tuple. Convert with `tolist(...)` if needed.
  2. For key-based access use `lookup()` or `local.map[key]` instead of index().
  3. If the source is a set and order matters, materialize an ordered list.

Example fix

// before
locals { i = index(var.tags_map, "env") }   # map passed

// after
locals { i = index(var.tags_list, "env") }   # list(string)
Defensive patterns

Strategy: validation

Validate before calling

# HCL: check the first arg is a list/tuple
locals { i = can(length(var.input)) && try(tolist(var.input), null) != null ? index(var.input, target) : -1 }

Type guard

# HCL: convert to list explicitly
locals { i = index(tolist(var.input), target) }

Prevention

When it happens

Trigger: Calling `index()` with a non-list first argument: `index({a=1,b=2}, 1)`, `index(var.amap, key)`, or `index("abc", "b")`. Sets are also rejected because they are unordered.

Common situations: Confusing index() (position in list) with lookup() (key in map). Passing a set where ordering is required. Mistyping a variable from list to set during refactor.

Related errors


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

Appendix: 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 d32a084675)