How to Fix Terraform 'Tuple Element Type Consistency' Error: Mixed Types in Tuple Literals
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5 mins
TL;DR
- What broke: Terraform's type system enforces that all elements in a tuple literal must resolve to the same type. Mixing
string,number, orboolin a single tuple orlist()call causes an immediateterraform planfailure. - How to fix it: Coerce all elements to a consistent type using
tostring(),tonumber(), or restructure as a typedobject/mapif heterogeneous data is genuinely required. - Use our Client-Side Sandbox above to paste your failing
.tfblock and auto-refactor it without leaking your config.
The Incident (What Does the Error Mean?)
Raw error output from terraform plan or terraform validate:
Error: Invalid value for input variable
on main.tf line 14, in variable "instance_config":
14: default = ["t3.micro", 2, true]
Inconsistent value types: all list elements must have the same type.
Elements of a tuple must have consistent types.
Terraform's type system is structural and strict. When you write ["t3.micro", 2, true], the Terraform evaluator attempts to infer a single element type for the collection. It finds string, number, and bool — three distinct primitive types — and hard-fails. There is no implicit coercion. The plan never generates. No resources are evaluated. The entire run is dead on arrival.
The Attack Vector / Blast Radius
This is a pipeline blocker, not a runtime warning. The blast radius:
- CI/CD pipelines fail immediately at the
validateorplanstage. Every downstream job —apply, drift detection, cost estimation — is skipped. - In module registries, a published module with this defect breaks every consumer workspace simultaneously on upgrade.
- Variable defaults with mixed types are a common copy-paste artifact when porting configs from YAML/JSON (which allows heterogeneous arrays) into HCL (which does not).
- Root module failures cascade: if the broken variable feeds a
for_eachordynamicblock, Terraform cannot build the resource graph at all, meaning zero resources are reconciled — including unrelated, healthy resources in the same root module run.
The longer this sits unfixed in a shared module, the more workspaces are blocked.
How to Fix It (The Solution)
Basic Fix — Coerce to a Consistent Type
The fastest resolution is to cast all elements to string. This is appropriate for display values, tags, or config maps consumed as strings downstream.
- default = ["t3.micro", 2, true]
+ default = ["t3.micro", "2", "true"]
Or use explicit tostring() coercion inside the expression:
- locals {
- config_tuple = [var.instance_type, var.instance_count, var.enable_monitoring]
- }
+ locals {
+ config_tuple = [tostring(var.instance_type), tostring(var.instance_count), tostring(var.enable_monitoring)]
+ }
Enterprise Best Practice — Use a Typed object Instead
If you genuinely need heterogeneous data (a string, a number, and a bool together), a tuple is the wrong construct. Use a typed object, which explicitly declares each field's type. This is self-documenting, validates at plan time, and survives refactoring.
- variable "instance_config" {
- default = ["t3.micro", 2, true]
- }
+ variable "instance_config" {
+ type = object({
+ instance_type = string
+ instance_count = number
+ enable_monitoring = bool
+ })
+ default = {
+ instance_type = "t3.micro"
+ instance_count = 2
+ enable_monitoring = true
+ }
+ }
Consumers then reference var.instance_config.instance_type, var.instance_config.instance_count, etc. — fully typed, no ambiguity, no silent coercion bugs.
For list variables that must be homogeneous:
- variable "allowed_ports" {
- default = [80, 443, "8080"]
- }
+ variable "allowed_ports" {
+ type = list(number)
+ default = [80, 443, 8080]
+ }
Adding an explicit type constraint forces Terraform to validate inputs at parse time and surfaces mismatches before they reach the provider API.
💡 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
1. terraform validate as a pre-commit gate
Add to .pre-commit-config.yaml:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.92.0
hooks:
- id: terraform_validate
- id: terraform_tflint
2. TFLint with the AWS/GCP ruleset
TFLint catches type inconsistency errors statically without requiring provider credentials:
tflint --init && tflint --recursive
3. Checkov policy for typed variable enforcement
Checkov's CKV_TF_1 and custom graph checks can flag variables missing explicit type blocks — the root cause of most tuple type errors:
checkov -d . --framework terraform
4. OPA/Conftest policy to enforce explicit typing
# deny variables without explicit type declarations
deny[msg] {
resource := input.variable[name]
not resource.type
msg := sprintf("Variable '%v' must declare an explicit type constraint.", [name])
}
Run in CI: conftest test --policy ./policies main.tf
5. Enforce in Terraform Cloud/Enterprise via Sentinel policies that reject plans where variable defaults contain untyped tuple literals. Lock this at the workspace policy set level so no operator can bypass it.