Fix Terraform Provider Version Conflicts
A practical guide to resolving Terraform provider version conflicts and lock file checksum errors across modules, developers, and CI pipelines.
Key takeaways
A practical guide to resolving Terraform provider version conflicts and lock file checksum errors across modules, developers, and CI pipelines.
On this page
Provider version problems are some of the most confusing errors Terraform throws, mostly because the message rarely points at the file you actually need to change. The plan fails, CI turns red, and the constraint it complains about lives three modules deep. Once you understand how Terraform picks a provider version and what the lock file is doing, these stop being mysteries.
How Terraform selects provider versions#
Every provider your configuration touches is declared in a required_providers block, usually inside a terraform block:
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40"
}
random = {
source = "hashicorp/random"
version = ">= 3.5.0"
}
}
}
When you run terraform init, Terraform collects the constraints for each provider from the root module and every child module, intersects them, and picks the newest published version that satisfies all of them. It then records the exact chosen version and its checksums in .terraform.lock.hcl, the dependency lock file. On subsequent runs Terraform reads that lock file first and reuses the pinned version instead of re-solving, which is what keeps two engineers on the same version.
The constraint operators matter here:
= 5.40.0pins one exact version.>= 5.40allows that version or anything newer.~> 5.40is the pessimistic operator: it allows5.40.xbut not5.41.0. Written~> 5.0, it allows any5.x.- No operator (
5.40.0) is treated as=.
Rule of thumb: use ~> for a sensible upgrade window in reusable modules, and let the lock file pin the exact build for reproducibility.
"No available releases match the given constraints"#
This is the conflict error. It almost always means two places in your dependency tree disagree. A common case: your root pins aws = "~> 5.40" while a shared module you pulled in still requires aws = "~> 4.0". There is no single AWS provider version that is both 5.x and 4.x, so the solver gives up.
To find the culprit, list where the constraints come from:
terraform providers
That prints a tree showing each module and the provider versions it asks for. Look for the module whose range does not overlap with the rest. The fix is to widen or align the constraints so an intersection exists, usually by upgrading the older module or loosening an overly strict pin. If the conflicting module is a third-party one you can't edit, you're stuck on a version that satisfies its range until it's updated, so that's often the signal to fork it or replace it.
"Provider was previously installed but now missing from the lock file" / checksum mismatch#
This one shows up after someone bumps a version or hand-edits the lock file. Terraform sees a provider in your config, finds an entry (or a partial entry) in .terraform.lock.hcl that no longer matches what it wants to install, and refuses to proceed rather than silently swapping binaries.
If the change is intentional, let Terraform move within your constraints and rewrite the lock file:
terraform init -upgrade
-upgrade tells Terraform to ignore the currently locked versions and re-select the newest allowed by your constraints, then update the checksums. Commit the resulting .terraform.lock.hcl. If it's not intentional (someone committed a bad merge of the lock file), reset the file from version control and run a plain terraform init.
Platform (H1:) checksum errors in CI#
Here's the one that catches teams out. You develop on a Mac, generate the lock file locally, commit it, and CI on Linux fails with something like "the local package does not match any of the checksums" or "Failed to install provider." The lock file only recorded the checksums for darwin_arm64, so when the Linux runner tries to install the linux_amd64 build, its hash isn't in the file.
The lock file can hold checksums for many platforms at once. Generate them for every OS/arch your team and pipelines use:
terraform providers lock \
-platform=linux_amd64 \
-platform=darwin_arm64 \
-platform=darwin_amd64 \
-platform=windows_amd64
This fetches each provider for each listed platform and writes all the H1: checksums into .terraform.lock.hcl. Commit the result and the CI checksum error goes away. Add this to your onboarding docs and re-run it whenever you bump a provider, so a laptop-only checksum never sneaks into the repo again.
Should you commit .terraform.lock.hcl? Yes#
Commit it. The lock file is the mechanism that guarantees every developer and every pipeline run installs the identical provider builds. Leaving it out of version control reintroduces the version drift you were trying to avoid, where one engineer plans against AWS provider 5.40.1 and another against 5.44.0, producing plan diffs that have nothing to do with the actual change.
The .terraform/ directory (the downloaded provider binaries and cached modules) is different. That is a local cache and belongs in .gitignore, never in the repo.
When things get wedged#
Sometimes init keeps failing even after you've fixed the constraints and the lock file, usually because the local cache is in a half-installed state. Clear it and start clean:
rm -rf .terraform/
terraform init
Deleting .terraform/ is safe: it only removes downloaded binaries and cached modules, not your state or your lock file. Terraform re-downloads everything on the next init. If you still hit a checksum wall after that, re-run the multi-platform terraform providers lock above so the fresh install matches what's recorded.
For the wider set of init, plan, and state failures beyond provider versions, see the Terraform error troubleshooting guide. If you're weighing the ecosystem more broadly, our Terraform vs OpenTofu comparison is a useful companion.
The call we'd make#
Pin providers with ~> in your required_providers blocks, commit .terraform.lock.hcl, and gitignore .terraform/. Standardize on running terraform providers lock with every platform your team and CI use, so the lock file is complete the day it's committed rather than the day CI first breaks. When a conflict appears, run terraform providers to find the disagreeing module before touching anything, and reach for terraform init -upgrade only when you actually intend to move versions. Treat the lock file as a first-class artifact, review changes to it in pull requests, and most of these errors simply stop happening.
Get the DevOps Troubleshooting Cheat Sheet
Subscribe and get our free one-page reference for the errors that eat an afternoon — CrashLoopBackOff, OOMKilled, Terraform state locks, and more — plus new guides as we publish them.
Fix the "Cycle" Dependency Error in Terraform
A "Cycle" error means two Terraform resources depend on each other; here is how to find the loop and break it cleanly.
OWASP Top 10 for LLM Applications (2026)
A working security engineer's tour of the ten failure modes unique to LLM apps, each paired with a fix you can ship this sprint.
More from DevOps
Explore more articles in this category
Best Log Management Tools in 2026: What You Actually Pay For
Every log platform looks affordable at proof-of-concept volume and expensive at production volume. The pricing model, not the feature list, decides which one you can live with.
Best Managed Kubernetes in 2026: EKS vs GKE vs AKS vs DOKS
The control plane fee is the least interesting number. What separates managed Kubernetes providers is upgrade cadence, how much they run for you, and where the node bill lands.
Your CI Runner Is the Target: Hardening Against npm Worms
The keyv compromise reached 444 packages and over two billion monthly installs through preinstall scripts. The controls that actually stop it are boring and mostly free.
You might have missed
Evergreen posts worth revisiting.