Terraform Error Guide: 'Invalid default value for variable' — Align the Default With the Type Constraint
Fix 'Invalid default value for variable' in Terraform by matching the default to its type constraint: string vs number, list vs set, and object attributes.
- #terraform
- #iac
- #troubleshooting
- #errors
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
Terraform validates every variable block during the earliest configuration-loading phase, before it plans or refreshes anything. If a variable declares a type constraint and also a default, Terraform checks that the default is convertible to that type. When it is not, evaluation stops immediately with a diagnostic that points at the offending default line:
Error: Invalid default value for variable
on variables.tf line 4, in variable "instance_count":
4: default = "three"
This default value is not compatible with the variable's type constraint:
a number is required.
The exact tail of the message changes with the constraint — a number is required, a bool is required, list of string required, attribute "port" is required, and so on — but the header Invalid default value for variable is constant. Because this runs so early, none of your resources, data sources, or provider blocks are even parsed yet: Terraform refuses to proceed until the declared default and the declared type agree.
Symptoms
terraform plan,terraform validate, orterraform applyaborts instantly withInvalid default value for variable, before any provider is configured.- The diagnostic names a specific
variableblock and itsdefault =line. - The final sentence states what type was required (
a number is required,a bool is required,list of object required, etc.). - Passing the same value in explicitly via
-varor a.tfvarsfile produces the sibling errorInvalid value for input variableinstead. - Removing the
typeconstraint (or removing thedefault) makes the error disappear — confirming it is a type/default mismatch, not a syntax problem.
Common Root Causes
- Quoted numbers or booleans.
default = "3"ordefault = "true"where the type isnumber/bool. Strings do not auto-convert to numbers here. - Wrong collection kind. Supplying a
listdefault for aset(string), or amapdefault for anobject(...), or[]where amapis required. - Object attribute mismatch. An
object({ ... })type whose default is missing a required attribute, has an extra unknown attribute, or gives an attribute the wrong element type. nullagainst a non-nullable type.default = nullwhen the variable is declarednullable = false.- Empty-collection ambiguity.
default = {}for alist(...)type (or[]for amap(...)) — the empty literal is the wrong collection kind. optional()misuse. Marking an attributeoptional()but then supplying a default object that still omits a required (non-optional) attribute.
Diagnostic Workflow
Run validate first — it surfaces the error without touching state or a backend:
terraform init -backend=false
terraform validate
terraform console # interactively test what a value converts to
Inspect the variable declaration and its default side by side. A classic failing case — a number type with a quoted string default:
variable "instance_count" {
type = number
default = "three" # string, not convertible to number
}
An object type whose default omits a required attribute:
variable "network" {
type = object({
name = string
cidr = string
port = number
})
default = {
name = "core"
cidr = "10.0.0.0/16"
# port is missing -> "attribute \"port\" is required"
}
}
Use terraform console to prove what a literal converts to before you commit it to the file:
echo 'tonumber("three")' | terraform console # error: cannot convert
echo 'tonumber("3")' | terraform console # 3
echo 'toset(["a","a","b"])' | terraform console
Example Root Cause Analysis
A team parameterized an autoscaling group and wanted the count to be tunable, so they wrote:
variable "instance_count" {
type = number
default = "3"
}
terraform validate failed with a number is required. The author assumed HCL would coerce the quoted "3" the way many templating languages do. It does not: the default is evaluated against the number constraint literally, and a string value "3" is not the same as the number 3. The fix is to drop the quotes so the literal is a number:
variable "instance_count" {
type = number
default = 3
}
The same class of bug bit a second variable, tags, declared as map(string) but defaulted to a list:
variable "tags" {
type = map(string)
default = ["env", "prod"] # list, not a map
}
Corrected to a map literal:
variable "tags" {
type = map(string)
default = {
env = "prod"
}
}
After both edits, terraform validate returned Success! The configuration is valid. and plan proceeded normally.
Prevention Best Practices
- Never quote numbers or booleans you intend as
number/booldefaults — write3andtrue, not"3"and"true". - Match the collection kind exactly:
[]only forlist/set/tuple,{}only formap/object. Usetype = list(string)withdefault = []andtype = map(string)withdefault = {}. - Keep object defaults in lockstep with the object type — every required (non-
optional()) attribute must appear, with the right element type, and no unknown attributes. - Use
optional(type, fallback)for attributes that genuinely may be absent, instead of hand-rolling partial default objects. - Run
terraform validatein CI on every push so a type/default mismatch is caught before review, not at apply time. - Prefer explicit types on every variable; an untyped variable accepts anything and simply defers the error to where the value is used, which is harder to debug.
Quick Command Reference
# Validate configuration without a backend or state
terraform init -backend=false
terraform validate
# Machine-readable diagnostics (useful in CI)
terraform validate -json
# Test what a literal converts to before editing the file
terraform console
# > tonumber("3")
# > toset(["a","b"])
# > { name = "core", port = 8080 }
# Format the file so structural mistakes are easier to spot
terraform fmt
# Re-plan once the default matches its type
terraform plan
Conclusion
Invalid default value for variable is a pure type-mismatch check that runs before Terraform does any real work: the value in default must be convertible to the variable’s declared type. The overwhelmingly common causes are quoting numbers or booleans, using the wrong collection kind ([] vs {}), and object defaults that omit a required attribute. Read the last line of the diagnostic — it tells you exactly what type was required — then either fix the literal or adjust the type constraint (including optional() for attributes that may be absent). Wire terraform validate into CI and these mismatches never reach an apply.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.