{"record":{"id":"bff2a95a9a0f7079","repo":"hashicorp/terraform","slug":"argument-must-be-a-string-a-collection-type-or-a","errorCode":null,"errorMessage":"argument must be a string, a collection type, or a structural type","messagePattern":"argument must be a string, a collection type, or a structural type","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/lang/funcs/collection.go","lineNumber":35,"sourceCode":")\n\nvar LengthFunc = function.New(&function.Spec{\n\tParams: []function.Parameter{\n\t\t{\n\t\t\tName:             \"value\",\n\t\t\tType:             cty.DynamicPseudoType,\n\t\t\tAllowDynamicType: true,\n\t\t\tAllowUnknown:     true,\n\t\t\tAllowMarked:      true,\n\t\t},\n\t},\n\tType: func(args []cty.Value) (cty.Type, error) {\n\t\tcollTy := args[0].Type()\n\t\tswitch {\n\t\tcase collTy == cty.String || collTy.IsTupleType() || collTy.IsObjectType() || collTy.IsListType() || collTy.IsMapType() || collTy.IsSetType() || collTy == cty.DynamicPseudoType:\n\t\t\treturn cty.Number, nil\n\t\tdefault:\n\t\t\treturn cty.Number, errors.New(\"argument must be a string, a collection type, or a structural type\")\n\t\t}\n\t},\n\tRefineResult: refineNotNull,\n\tImpl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {\n\t\tcoll := args[0]\n\t\tcollTy := args[0].Type()\n\t\tmarks := coll.Marks()\n\n\t\tswitch {\n\t\tcase collTy == cty.DynamicPseudoType:\n\t\t\treturn cty.UnknownVal(cty.Number).WithMarks(marks), nil\n\t\tcase collTy.IsTupleType():\n\t\t\tl := len(collTy.TupleElementTypes())\n\t\t\treturn cty.NumberIntVal(int64(l)).WithMarks(marks), nil\n\t\tcase collTy.IsObjectType():\n\t\t\tl := len(collTy.AttributeTypes())\n\t\t\treturn cty.NumberIntVal(int64(l)).WithMarks(marks), nil\n\t\tcase collTy == cty.String:","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/hashicorp/terraform/blob/c9def3e214014c1188faabfc4a5bde5095139765/internal/lang/funcs/collection.go#L17-L53","documentation":"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.","triggerScenarios":"Calling length() on a scalar: length(5), length(true), or a variable typed as number/bool.","commonSituations":"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.","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."],"exampleFix":"# before\noutput \"n\" { value = length(var.replica_count) }   # replica_count is a number -> error\n# after\noutput \"n\" { value = length(split(\"\", tostring(var.replica_count))) }   # count digits\n# or, if you meant a count of resources, pass the actual collection:\noutput \"n\" { value = length(aws_instance.replicas) }","handlingStrategy":"type-guard","validationCode":"# ensure the argument is a collection/string before calling length()\nlocals {\n  len_or_null = can(length(var.x)) ? length(var.x) : null\n}","typeGuard":"# type guard: only call length on values that can be measured\nlocals {\n  ok  = can(length(var.x))\n  res = local.ok ? length(var.x) : null\n}","tryCatchPattern":"locals { len = try(length(var.x), null) }","preventionTips":["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."],"tags":["hcl","function","length","type-validation"],"analyzedSha":"c9def3e214014c1188faabfc4a5bde5095139765","analyzedAt":"2026-08-07T15:39:49.278Z","schemaVersion":2},"datasetVersion":"2026-08-07T21:17:07.882Z"}