How to Fix 'The condition value is not a valid boolean' in Terraform Variable Validation
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5 mins
TL;DR
- What broke: Terraform's
validationblock requires itsconditionattribute to resolve to a strict boolean (true/false). Your expression is returning a string, number, list, ornull— Terraform hard-fails atterraform validate/plan. - How to fix it: Wrap the offending expression in a boolean-casting function (
can(),contains(),length(...) > 0, or a direct comparison operator) so the condition always resolves totrueorfalse. - Fast path: Use our Client-Side Sandbox above to paste your variable block and auto-refactor the condition expression instantly.
The Incident (What Does the Error Mean?)
Raw error emitted by terraform validate or terraform plan:
Error: Invalid validation condition
on variables.tf line 8, in variable "instance_type":
8: condition = var.instance_type
The condition value is not a valid boolean. Terraform requires a boolean
true or false value for the condition in a validation block.
Immediate consequence: Terraform exits with a non-zero code. Every downstream stage — plan, apply, CI pipeline, Atlantis run, Spacelift job — is dead on arrival. No state is modified, but no infrastructure ships either. In a GitOps workflow, this blocks the entire merge queue.
The Attack Vector / Blast Radius
This is not a runtime error — it is a schema-level rejection. Terraform's type system evaluates condition at parse/validate time. The blast radius:
- All environments simultaneously. Because
variables.tfis typically shared acrossdev,staging, andprodworkspaces, every workspace using this module fails to plan. - Module consumers are blocked. If this variable lives inside a reusable module published to a private registry, every team consuming that module version is broken until a patched version is released.
- Silent misconfiguration risk. The most dangerous variant is when the condition looks boolean but returns a string like
"true"(a Terraformstringtype, not the booleantrue). This passes local syntax checks but fails in strict validation, causing intermittent CI failures that are hard to bisect. can()misuse cascades. Engineers who wrap the entire condition incan()to silence the error without understanding the root cause create a condition that always returnstrue, effectively disabling the validation entirely — a silent security regression.
How to Fix It (The Solution)
Root Cause Taxonomy
| Expression Pattern | Actual Return Type | Fix |
|---|---|---|
var.some_string |
string |
Use contains([...], var.x) or var.x == "value" |
regex("pattern", var.x) |
string (matched text) |
Wrap in can(regex(...)) |
length(var.x) |
number |
Use length(var.x) > 0 |
lookup(map, key, null) |
any / null |
Use lookup(map, key, null) != null |
var.flag where flag is string typed |
string "true"/"false" |
Change var type to bool or use var.flag == "true" |
Basic Fix — Enum String Validation
variable "instance_type" {
type = string
description = "EC2 instance type"
validation {
- condition = var.instance_type
+ condition = contains(["t3.micro", "t3.small", "t3.medium"], var.instance_type)
error_message = "instance_type must be one of: t3.micro, t3.small, t3.medium."
}
}
Basic Fix — Regex Pattern Validation
variable "environment" {
type = string
validation {
- condition = regex("^(dev|staging|prod)$", var.environment)
+ condition = can(regex("^(dev|staging|prod)$", var.environment))
error_message = "environment must be dev, staging, or prod."
}
}
Why
can()?regex()returns the matched string on success and throws an error on no match.can()catches that error and returnsfalse, which is the boolean Terraform needs.
Basic Fix — Numeric Range Validation
variable "replica_count" {
type = number
validation {
- condition = var.replica_count
+ condition = var.replica_count >= 1 && var.replica_count <= 10
error_message = "replica_count must be between 1 and 10 inclusive."
}
}
Enterprise Best Practice — Composite Multi-Condition Validation with Type-Safe Guards
For production modules, never rely on a single condition. Layer them and use alltrue() for readability and auditability:
variable "cidr_block" {
type = string
description = "VPC CIDR block. Must be a valid /16 to /28 range."
validation {
- condition = var.cidr_block
+ condition = alltrue([
+ can(cidrhost(var.cidr_block, 0)),
+ tonumber(split("/", var.cidr_block)[1]) >= 16,
+ tonumber(split("/", var.cidr_block)[1]) <= 28
+ ])
error_message = "cidr_block must be a valid CIDR notation between /16 and /28."
}
}
Why this pattern is production-grade:
can(cidrhost(...))validates the CIDR is syntactically valid before the numeric comparisons run — short-circuits on malformed input.alltrue([...])makes each sub-condition independently auditable in code review.- No condition ever returns a non-boolean type; each sub-expression is a comparison or
can()call.
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
Do not let this reach a human code review. Catch it at commit time.
1. terraform validate as a Pre-Commit Hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.88.0
hooks:
- id: terraform_validate
- id: terraform_tflint
terraform validate will catch non-boolean conditions before the branch is pushed.
2. TFLint with the AWS/GCP Ruleset
# .tflint.hcl
plugin "terraform" {
enabled = true
version = "0.5.0"
source = "github.com/terraform-linters/tflint-ruleset-terraform"
}
rule "terraform_required_version" { enabled = true }
rule "terraform_typed_variables" { enabled = true } # catches untyped vars that produce ambiguous condition types
3. Checkov Policy — Enforce Validation Blocks Exist
checkov -d . --check CKV_TF_1 --compact
For custom enforcement, add an OPA/Rego policy to your Conftest suite:
# policies/variable_validation.rego
package terraform.validation
deny[msg] {
var := input.variable[name]
validation := var.validation[_]
# Flag if condition is a bare variable reference (no operator)
validation.condition == concat("", ["var.", name])
msg := sprintf("Variable '%s': condition is a bare variable reference, not a boolean expression.", [name])
}
conftest test --policy policies/ plan.json
4. GitHub Actions Gate
# .github/workflows/terraform-validate.yml
name: Terraform Validate
on: [pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: "~1.8"
- run: terraform init -backend=false
- run: terraform validate
# Fails the PR if any condition is non-boolean
The rule: terraform validate must be a required status check on your default branch. No exceptions. A non-boolean condition in a shared module is a 30-second fix that becomes a 3-hour incident when it ships to a module registry consumed by 12 teams.