hashicorp/terraform · error

cannot decode tfvars from a null value

Error message

cannot decode tfvars from a null value

What it means

Emitted by decodeTfvarsFunc (functions.go:98-100) when the argument is typed as string but its value is null. Parsing tfvars syntax from a null string is meaningless, so it is rejected explicitly after the type check. The encode counterpart has a symmetric 'cannot encode a null value' check (functions.go:38-42).

Source

Thrown at internal/builtin/providers/terraform/functions.go:99

	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())

	// As usual when we wrap HCL stuff up in functions, we end up needing to
	// stuff HCL diagnostics into plain string error messages. This produces
	// a non-ideal result but is still better than hiding the HCL-provided

View on GitHub (pinned to c9def3e214)

Solutions

  1. Provide a default of "" (empty string) for the input variable so it is never null: `variable "x" { type = string, default = "" }`.
  2. Guard with coalesce: `decode_tfvars(coalesce(var.optional_tfvars, ""))`.
  3. Skip the decode entirely when the value is null using a conditional.

Example fix

// before
locals { out = decode_tfvars(var.maybe_tfvars) }  // null when unset -> error

// after
variable "maybe_tfvars" { type = string, default = "" }
locals { out = decode_tfvars(var.maybe_tfvars) }
Defensive patterns

Strategy: type-guard

Validate before calling

// Reject null before parsing.
if args[0].IsNull() { return cty.NilVal, errors.New("decode_tfvars cannot decode a null string") }

Type guard

// Narrow to a known-non-null string before calling decode_tfvars.
func isNonEmptyString(v cty.Value) bool { return v.Type() == cty.String && v.IsKnown() && !v.IsNull() }

Prevention

When it happens

Trigger: Calling decode_tfvars on an optional variable that was not set (resolving to null), e.g. `decode_tfvars(var.optional_tfvars)` where var.optional_tfvars has no default and is unset.

Common situations: An optional input variable of type string with no default passed straight to decode_tfvars; a lookup that returns null; nullable module inputs.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/3aebf8bfe6eaaef7. Report an issue: GitHub.