hashicorp/terraform · error
invalid expression for variable %q: %s
Error message
invalid expression for variable %q: %s
What it means
Thrown by `decode_tfvars` (functions.go:134) when a top-level attribute parses but its expression cannot be evaluated with a nil EvalContext. Because `decode_tfvars` evaluates each attribute with no evaluation context, any function call, variable reference, or unknown symbol makes `attr.Expr.Value(nil)` fail, and the attribute name plus the HCL diagnostic are returned.
Source
Thrown at internal/builtin/providers/terraform/functions.go:134
// stuff HCL diagnostics into plain string error messages. This produces
// a non-ideal result but is still better than hiding the HCL-provided
// diagnosis altogether.
f, hclDiags := hclsyntax.ParseConfig(src, "<decode_tfvars argument>", hcl.InitialPos)
if hclDiags.HasErrors() {
return cty.NilVal, fmt.Errorf("invalid tfvars syntax: %s", hclDiags.Error())
}
attrs, hclDiags := f.Body.JustAttributes()
if hclDiags.HasErrors() {
return cty.NilVal, fmt.Errorf("invalid tfvars content: %s", hclDiags.Error())
}
retAttrs := make(map[string]cty.Value, len(attrs))
for name, attr := range attrs {
// Evaluating the expression with no EvalContext achieves the same
// interpretation as Terraform CLI makes of .tfvars files, rejecting
// any function calls or references to symbols.
v, hclDiags := attr.Expr.Value(nil)
if hclDiags.HasErrors() {
return cty.NilVal, fmt.Errorf("invalid expression for variable %q: %s", name, hclDiags.Error())
}
retAttrs[name] = v
}
return cty.ObjectVal(retAttrs), nil
}
func encodeExprFunc(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")
}
View on GitHub (pinned to c9def3e214)
Solutions
- Replace every function call or reference with its already-computed literal value before passing the string to `decode_tfvars`.
- If you need evaluated tfvars, load the file normally via the root module's variable declarations instead of through `decode_tfvars`.
- Use `encode_tfvars`/`encode_expr` to produce literal-only strings you can safely round-trip back.
- Scan the source for `(` , `[a-z].*\.` references in a pre-check (see defense) to fail fast.
Example fix
// before
decode_tfvars("region = upper(var.region)")
// after
decode_tfvars("region = "US-EAST-1"") Defensive patterns
Strategy: try-catch
Validate before calling
// Heuristic pre-check: reject obvious function calls and references.
var exprish = regexp.MustCompile(`(?m)^\s*[A-Za-z_][\w.-]*\s*=\s*[A-Za-z_][\w]*\s*\(`)
func hasLikelyCallOrRef(src string) bool { return exprish.MatchString(src) } Try / catch
val, err := provider.CallFunction(providers.CallFunctionRequest{
FunctionName: "decode_tfvars",
Arguments: []cty.Value{cty.StringVal(raw)},
})
if err != nil {
// attribute-level parse failure; surface err to the user verbatim,
// it already names the offending variable.
return fmt.Errorf("decode_tfvars rejected input: %w", err)
} Prevention
- Only ever put literal values (strings, numbers, bools, lists, maps) in strings fed to decode_tfvars.
- Pre-evaluate any interpolation before constructing the tfvars string.
- When round-tripping objects, prefer encode_tfvars so the output is guaranteed literal-only.
When it happens
Trigger: The tfvars string contains a function call such as `name = upper("x")`, a reference such as `name = var.region`, or any identifier that is not a literal. These are legal in normal `.tfvars` evaluation against a configured root module but are rejected here because `decode_tfvars` is a pure literal parser.
Common situations: Authoring tfvars that rely on interpolation and expecting `decode_tfvars` to resolve them; round-tripping an object that included function calls; feeding tfvars produced by tooling that emits references.
Related errors
- invalid tfvars content: %s
- Cannot set both 'source' and 'content'
- Must provide one of 'source' or 'content'
- invalid null string in 'scripts'
- invalid empty string in 'scripts'
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/7c37c8c83d0fc61a.
Report an issue: GitHub.