hashicorp/terraform · error

cannot decode tfvars from a null value

Error message

cannot decode tfvars from a null value

What it means

decode_tfvars refuses to parse a null input because there is no tfvars content to decode. The function's declared parameter type is non-nullable cty.String, so reaching this branch means the language runtime failed to enforce the schema. It is a defensive guard, not a normal user-facing path.

Solutions

  1. Ensure the caller honors the function schema (Parameter Type=cty.String, no AllowNull) so null never reaches the function
  2. Coalesce the input before calling: decode_tfvars(coalesce(var.maybe_null, ""))
  3. If invoking from Go, guard args[0].IsNull() (and args[0].IsKnown()) before calling decodeTfvarsFunc

Example fix

// before
decode_tfvars(var.maybe_null)
// after
decode_tfvars(coalesce(var.maybe_null, ""))
Defensive patterns

Strategy: validation

Validate before calling

// In Go, before invoking decodeTfvarsFunc directly:
if len(args) != 1 || args[0].IsNull() || !args[0].IsKnown() || args[0].Type() != cty.String {
    return cty.NilVal, fmt.Errorf("decode_tfvars requires one known, non-null string")
}
// In HCL, ensure a non-null input before the call:
// decode_tfvars(coalesce(var.maybe_null, ""))

Type guard

// HCL-level guard: guarantee a non-null string reaches decode_tfvars
locals {
  safe_src = var.maybe_null == null ? "" : var.maybe_null
}
// then: decode_tfvars(local.safe_src)

Prevention

When it happens

Trigger: Calling decode_tfvars(null) or decode_tfvars(var.x) where var.x resolves to null and the runtime did not short-circuit; invoking decodeTfvarsFunc directly from Go with cty.NullVal(cty.String) as args[0].

Common situations: A Terraform Core / plugin dispatch bug that ignores the function's parameter schema; direct unit tests calling the Go function with a null cty.Value instead of routing through the runtime.

Related errors


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

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