hashicorp/terraform · error
argument must be a string
Error message
argument must be a string
What it means
Emitted by decodeTfvarsFunc (functions.go:95-97) when the single argument's cty type is not cty.String. decode_tfvars parses HCL tfvars syntax from a string; a non-string value (number, list, object, bool) cannot be parsed and is rejected after the arity check but before null/known checks.
Source
Thrown at internal/builtin/providers/terraform/functions.go:96
body.SetAttributeValue(key, v)
}
result := f.Bytes()
return cty.StringVal(string(result)), nil
}
func decodeTfvarsFunc(args []cty.Value) (cty.Value, error) {
// These error checks should not be hit in practice because the language
// runtime should check them before calling, so this is just for robustness
// and completeness.
if len(args) > 1 {
return cty.NilVal, function.NewArgErrorf(1, "too many arguments; only one expected")
}
if len(args) == 0 {
return cty.NilVal, fmt.Errorf("exactly one argument is required")
}
if args[0].Type() != cty.String {
return cty.NilVal, fmt.Errorf("argument must be a string")
}
if args[0].IsNull() {
return cty.NilVal, fmt.Errorf("cannot decode tfvars from a null value")
}
if !args[0].IsKnown() {
// If our input isn't known then we can't even predict the result
// type, since it will be an object type decided based on which
// arguments and values we find in the string.
return cty.DynamicVal, nil
}
// If we get here then we know that:
// - there's exactly one element in args
// - it's a string
// - it is known and non-null
// So therefore the following is guaranteed to succeed.
src := []byte(args[0].AsString())
View on GitHub (pinned to c9def3e214)
Solutions
- Ensure the argument is a string, e.g. `decode_tfvars(var.tfvars_string)` or `decode_tfvars(file("terraform.tfvars"))` (file() returns a string).
- Add a type constraint `type = string` to the input variable feeding decode_tfvars.
- If the source is JSON, convert with `jsondecode` only after decoding tfvars, not before.
Example fix
// before
locals { out = decode_tfvars(filesize) } // filesize is a number
// after
locals { out = decode_tfvars(file("terraform.tfvars")) } Defensive patterns
Strategy: type-guard
Validate before calling
// Type-check the argument before parsing.
if args[0].Type() != cty.String {
return cty.NilVal, fmt.Errorf("decode_tfvars argument must be string, got %s", args[0].Type().FriendlyName())
} Type guard
// Narrow to a known-non-null string before calling decode_tfvars.
func isCtyString(v cty.Value) bool { return v.Type() == cty.String && !v.IsNull() && v.IsKnown() } Prevention
- Use file() (which returns string) or a type=string variable to feed decode_tfvars.
- Add `type = string` constraints to source variables so the runtime rejects mismatches early.
- Do not pass numbers/objects/lists; convert with tostring() only when meaningful.
- Remember tfvars is HCL, not JSON — use jsondecode for JSON inputs separately.
When it happens
Trigger: Calling decode_tfvars with a number, boolean, list, or object instead of a string, e.g. `decode_tfvars(42)` or `decode_tfvars({a=1})`. The schema declares the parameter as String, so the runtime normally type-coerces or rejects first; this is the defensive fallback.
Common situations: Forgetting to wrap the input in a string; passing a variable of the wrong type; a programmatic caller passing a non-string cty.Value that bypassed schema type enforcement.
Related errors
- cannot decode tfvars from a null value
- invalid tfvars syntax: %s
- argument must be a string, a collection type, or a structura
- all arguments must have the same type
- argument must be a list or tuple
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/711c7911f38a03d3.
Report an issue: GitHub.