hashicorp/terraform · error

invalid expression for variable

Error message

invalid expression for variable %q: %s

What it means

After extracting attributes, decode_tfvars evaluates each value expression with a nil EvalContext, matching Terraform CLI's .tfvars semantics which reject function calls and references to symbols. If an individual value expression references a symbol or invokes a function, this error names the offending variable.

Solutions

  1. Use only literal values in the tfvars string (numbers, strings, lists, objects) — no references and no function calls
  2. Compute derived values in Terraform first, then pass the literal result into the string
  3. If references are required, switch from a tfvars string to a proper module input or a different encoding

Example fix

// before
decode_tfvars("a = upper(\"x\")")
// after
decode_tfvars("a = \"X\"")
Defensive patterns

Strategy: validation

Validate before calling

// Evaluate each attribute expression with a nil EvalContext to surface reference/call errors early.
// In Go (mirrors decode_tfvars semantics):
attrs, _ := f.Body.JustAttributes()
for name, attr := range attrs {
    if _, diags := attr.Expr.Value(nil); diags.HasErrors() {
        // variable 'name' contains an illegal reference or call; report it
    }
}

Prevention

When it happens

Trigger: decode_tfvars("a = somefunc()") with a function call in a value; decode_tfvars("a = var.other") with a reference; decode_tfvars("a = local.x") referencing a local.

Common situations: Building a tfvars string dynamically that accidentally interpolates references; assuming tfvars values can reference other variables or call functions (they cannot); migrating a config snippet into a tfvars string without stripping references.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/7c37c8c83d0fc61a. Report an issue: GitHub.

Appendix: 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 d32a084675)