hashicorp/terraform · error
argument must be a string, a collection type, or a structura
Error message
argument must be a string, a collection type, or a structural type
What it means
Thrown by the Type function of LengthFunc (the HCL length() builtin). length() only accepts a string, tuple, object, list, map, set, or dynamic value. Any other concrete type (number, bool) fails type validation before the function body runs.
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 c9def3e214)
Solutions
- Convert first or pick the right function: length(tostring(var.n)) for digit/grapheme count, or use a different expression for booleans.
- Re-type the variable so it is a collection or string.
- Guard with can(length(var.x)) when the input type is uncertain.
Example fix
# before
output "n" { value = length(var.replica_count) } # replica_count is a number -> error
# after
output "n" { value = length(split("", tostring(var.replica_count))) } # count digits
# or, if you meant a count of resources, pass the actual collection:
output "n" { value = length(aws_instance.replicas) } Defensive patterns
Strategy: type-guard
Validate before calling
# ensure the argument is a collection/string before calling length()
locals {
len_or_null = can(length(var.x)) ? length(var.x) : null
} Type guard
# type guard: only call length on values that can be measured
locals {
ok = can(length(var.x))
res = local.ok ? length(var.x) : null
} Try / catch
locals { len = try(length(var.x), null) } Prevention
- Type variables explicitly (string, list(...), map(...)) so misuse is caught at validation.
- Prefer domain-specific counts (length(aws_instance.x)) over calling length on scalars.
- Add check blocks asserting collection types for shared locals.
When it happens
Trigger: Calling length() on a scalar: length(5), length(true), or a variable typed as number/bool.
Common situations: Assuming length() works on numbers (e.g., to count digits); passing a boolean flag; a variable whose type was narrowed to number by an upstream expression.
Related errors
- all arguments must have the same type
- argument must be a list or tuple
- keys and searchset must be of the same type
- input must be a map of lists of strings
- no non-null, non-empty-string arguments
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/bff2a95a9a0f7079.
Report an issue: GitHub.