Fixing Terraform Provider Version Conflicts
Resolve Terraform provider constraint conflicts using the requirement tree, required_providers, committed lock selections, checksums, and tested upgrades.
A Terraform (or OpenTofu) provider version conflict shows up as init failing with an unsatisfiable constraint, or a plan suddenly proposing unexpected changes after a provider update nobody explicitly requested.
Step 1: read the exact constraint conflict from init
terraform init
The error message names the specific providers and version constraints that can’t be satisfied together — this is more informative than it first looks, since it usually names exactly which module or configuration block is requiring an incompatible version.
Step 2: check the currently locked provider versions
cat .terraform.lock.hcl
The lock file records exactly which provider version was selected the last time init succeeded — comparing this against what your configuration now requires shows precisely what changed.
Step 3: check for a version constraint that’s too loose
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
# Broad for a root module: accepts the entire 5.x line.
version = "~> 5.0"
}
}
}
An overly loose constraint is often the actual root cause of “it worked yesterday, broke today” — a new provider release matching the loose constraint introduced a breaking change nobody explicitly opted into.
Step 4: pin to an exact, tested version
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "= 5.31.0"
}
}
}
Version requirements belong in required_providers; declaring them inside a provider configuration is deprecated. An exact requirement can be appropriate in a root configuration under strict change control, but reusable modules should generally declare the minimum compatible version and avoid unnecessarily constraining consumers. Reproducibility ultimately comes from the selected version and checksums in the lock file.
Step 5: update the lock file deliberately, not accidentally
terraform init -upgrade
This is the explicit, deliberate way to move to newer provider versions — running it unintentionally (rather than a plain terraform init) is a common cause of an unexpected provider version change reaching a team that wasn’t planning one.
Step 6: commit the updated lock file after verifying the new version works
git add .terraform.lock.hcl
git commit -m "chore: bump aws provider to 5.32.0 after testing"
The lock file is meant to be committed to version control specifically so every team member and CI run gets the identical, already-verified provider version — never .gitignore-d.
Generate additional platform checksums with terraform providers lock when teams or CI use multiple operating systems. Review checksum changes just like version changes. Do not hand-edit the lock file or accept a checksum mismatch without confirming the provider’s source and signature path.
Step 7: check for multiple modules requiring incompatible versions of the same provider
terraform providers
This shows the full provider requirement tree across your root module and any child modules — a conflict between two modules’ own internal constraints needs to be resolved by updating whichever module has the stricter, outdated requirement.
Step 8: use required_providers blocks consistently across modules
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.30"
}
}
}
Consistent, deliberately-chosen constraints across every module in a codebase substantially reduces the odds of two parts of the same project silently drifting toward incompatible requirements.
Upgrade and recover without hiding drift
First identify which module introduced each constraint with terraform providers, then update or replace that module deliberately. Deleting .terraform.lock.hcl merely asks Terraform to select again; it can hide the immediate conflict while producing an unreviewed provider change. Keep a copy of the prior lock file and use normal version-control review.
Run terraform init -upgrade on a branch, read provider and Terraform release notes, and generate a saved plan against a non-production or read-only context. Unexpected plan changes can be provider normalization or schema migration, but they can also reveal real drift. Explain every material change before apply and test rollback constraints where a provider downgrade is supported.
Automated dependency tooling can propose narrow upgrades, but CI should run formatting, validation, tests, and plans without automatically applying infrastructure from an unreviewed update. Providers execute code locally with the privileges and credentials of Terraform, so source authenticity is part of the security boundary.
Why committing the lock file is the single most important habit here
Without a committed lock file, every fresh terraform init — a new team member’s laptop, a CI pipeline, a fresh environment — can resolve to a different provider version that happens to satisfy the same loose constraints, producing “works on my machine” drift that’s genuinely difficult to diagnose. Treating .terraform.lock.hcl as a normal, always-committed part of the codebase is what actually makes provider versions reproducible across a team.
Related:
Sources: