{"record":{"id":"711c7911f38a03d3","repo":"hashicorp/terraform","slug":"argument-must-be-a-string","errorCode":null,"errorMessage":"argument must be a string","messagePattern":"argument must be a string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/builtin/providers/terraform/functions.go","lineNumber":96,"sourceCode":"\t\tbody.SetAttributeValue(key, v)\n\t}\n\n\tresult := f.Bytes()\n\treturn cty.StringVal(string(result)), nil\n}\n\nfunc decodeTfvarsFunc(args []cty.Value) (cty.Value, error) {\n\t// These error checks should not be hit in practice because the language\n\t// runtime should check them before calling, so this is just for robustness\n\t// and completeness.\n\tif len(args) > 1 {\n\t\treturn cty.NilVal, function.NewArgErrorf(1, \"too many arguments; only one expected\")\n\t}\n\tif len(args) == 0 {\n\t\treturn cty.NilVal, fmt.Errorf(\"exactly one argument is required\")\n\t}\n\tif args[0].Type() != cty.String {\n\t\treturn cty.NilVal, fmt.Errorf(\"argument must be a string\")\n\t}\n\tif args[0].IsNull() {\n\t\treturn cty.NilVal, fmt.Errorf(\"cannot decode tfvars from a null value\")\n\t}\n\tif !args[0].IsKnown() {\n\t\t// If our input isn't known then we can't even predict the result\n\t\t// type, since it will be an object type decided based on which\n\t\t// arguments and values we find in the string.\n\t\treturn cty.DynamicVal, nil\n\t}\n\n\t// If we get here then we know that:\n\t// - there's exactly one element in args\n\t// - it's a string\n\t// - it is known and non-null\n\t// So therefore the following is guaranteed to succeed.\n\tsrc := []byte(args[0].AsString())\n","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/hashicorp/terraform/blob/d32a084675427f5ac3f7d2868578ef8b2c1dc525/internal/builtin/providers/terraform/functions.go#L78-L114","documentation":"decode_tfvars builtin guard: the schema declares its single parameter as cty.String, so the runtime should reject non-string inputs first. This branch fires only when the function is invoked with a value whose cty type is not String, again a defensive check for direct/programmatic misuse.","triggerScenarios":"decodeTfvarsFunc invoked with args[0].Type() != cty.String: a direct call passing a number/list/object, or a dispatcher regression that skips schema type-checking.","commonSituations":"Programmatic calls with the wrong cty type; a regression in schema-driven type narrowing; experimental code passing dynamic-typed values directly.","solutions":["Pass a string to decode_tfvars (decode_tfvars(\"key = \\\"value\\\"\")) — coerce with tostring() if needed.","Programmatic callers must pass a cty.String value.","Verify the function's ParameterTypes when wrapping the function."],"exampleFix":"// before\nlocals { v = decode_tfvars({a = 1}) }\n// after\nlocals { v = decode_tfvars(\"a = 1\\n\") }","handlingStrategy":"type-guard","validationCode":"if args[0].Type() != cty.String {\n    // coerce with tostring() at the HCL level, or fail fast here\n    return cty.NilVal, fmt.Errorf(\"decode_tfvars requires a string, got %s\", args[0].Type().FriendlyName())\n}","typeGuard":"func decodeTfvarsArgIsString(args []cty.Value) bool {\n    return len(args) == 1 && args[0].Type() == cty.String\n}","tryCatchPattern":null,"preventionTips":["Pass only string values to decode_tfvars; use tostring() to coerce.","Keep the schema ParameterType and the in-body type check aligned.","Add a unit test passing each non-string cty type to confirm the guard."],"tags":["terraform","builtin","tfvars","decode","type-check","defensive"],"backgroundTag":null,"analyzedSha":"d32a084675427f5ac3f7d2868578ef8b2c1dc525","analyzedAt":"2026-08-11T18:43:52.779Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}