hashicorp/terraform · error
argument must be a string
Error message
argument must be a string
What it means
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.
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.
Example fix
// before
locals { v = decode_tfvars({a = 1}) }
// after
locals { v = decode_tfvars("a = 1\n") } Defensive patterns
Strategy: type-guard
Validate before calling
if args[0].Type() != cty.String {
// coerce with tostring() at the HCL level, or fail fast here
return cty.NilVal, fmt.Errorf("decode_tfvars requires a string, got %s", args[0].Type().FriendlyName())
} Type guard
func decodeTfvarsArgIsString(args []cty.Value) bool {
return len(args) == 1 && args[0].Type() == cty.String
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Programmatic calls with the wrong cty type; a regression in schema-driven type narrowing; experimental code passing dynamic-typed values directly.
Related errors
- exactly one argument is required
- argument must be a list or tuple
- argument must be a string, a collection type, or a…
- error in marshalPlanVariables
- Error reading as a statefile
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/711c7911f38a03d3.
Report an issue: GitHub.
Appendix: 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 d32a084675)