hashicorp/terraform · error

argument must be a string, a collection type, or a…

Error message

argument must be a string, a collection type, or a structural type

What it means

Thrown by the `length()` HCL function (LengthFunc) when its single argument's cty type is not one of string, tuple, object, list, map, set, or dynamic. The Type callback enumerates the accepted types and rejects everything else (numbers, booleans) at type-check time, before the implementation runs. Numbers and booleans have no meaningful length, so they are rejected outright.

Solutions

  1. Pass a value of an accepted type: string, list, set, map, tuple, or object. Inspect the variable declaration and confirm it is a collection.
  2. If you meant the magnitude of a number, numbers are used directly — remove the length() call.
  3. If the value is computed/unknown, ensure it is at least typed as a collection; dynamic values are allowed but a definitely-number type is not.

Example fix

// before
output "n" { value = length(var.replica_count) }   # var.replica_count is number

// after
output "n" { value = length(var.replica_names) }    # var.replica_names is list(string)
Defensive patterns

Strategy: validation

Validate before calling

# HCL: confirm the variable is a collection before calling length()
locals {
  ok = can([for v in var.input : v])  # true only for list/set/tuple
  n  = local.ok ? length(var.input) : 0
}

Type guard

# HCL guard using try() to fall back on non-collections
locals { n = try(length(var.input), 0) }

Prevention

When it happens

Trigger: Calling `length()` in an HCL/Terraform config with a number or boolean value, e.g. `length(var.replicas)` where var.replicas is `number`. The Type func switch falls to the default branch and returns this error.

Common situations: Mis-naming a variable so a scalar (count, bool flag, port number) is passed instead of the intended list/map. Refactoring a variable from list(number) to number and forgetting to update its `length()` call. Passing a single string char count where `strlen` semantics differ.

Related errors


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

Appendix: source

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

)

var LengthFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name:             "value",
			Type:             cty.DynamicPseudoType,
			AllowDynamicType: true,
			AllowUnknown:     true,
			AllowMarked:      true,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		collTy := args[0].Type()
		switch {
		case collTy == cty.String || collTy.IsTupleType() || collTy.IsObjectType() || collTy.IsListType() || collTy.IsMapType() || collTy.IsSetType() || collTy == cty.DynamicPseudoType:
			return cty.Number, nil
		default:
			return cty.Number, errors.New("argument must be a string, a collection type, or a structural type")
		}
	},
	RefineResult: refineNotNull,
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		coll := args[0]
		collTy := args[0].Type()
		marks := coll.Marks()

		switch {
		case collTy == cty.DynamicPseudoType:
			return cty.UnknownVal(cty.Number).WithMarks(marks), nil
		case collTy.IsTupleType():
			l := len(collTy.TupleElementTypes())
			return cty.NumberIntVal(int64(l)).WithMarks(marks), nil
		case collTy.IsObjectType():
			l := len(collTy.AttributeTypes())
			return cty.NumberIntVal(int64(l)).WithMarks(marks), nil
		case collTy == cty.String:

View on GitHub (pinned to d32a084675)