A practical guide to resolving Terraform provider version conflicts and lock file checksum errors across modules, developers, and CI pipelines.
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.
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.0 pins one exact version.>= 5.40 allows that version or anything newer.~> 5.40 is the pessimistic operator: it allows 5.40.x but not 5.41.0. Written ~> 5.0, it allows any 5.x.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.
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.
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.
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.
.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.
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.
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 latest tutorials, guides, and insights on AI, DevOps, Cloud, and Infrastructure delivered directly to your inbox.
Explore more articles in this category
Terraform's errors are scarier than the fixes. This is the map to the ones everyone hits: what each message means, the safe way out, and how to avoid losing state.
A practical guide to renaming resources, migrating backends, and splitting or merging Terraform state without destroying and recreating infrastructure.
Bring cloud resources that already exist under Terraform management without recreating them, using import blocks or the classic import command.
Evergreen posts worth revisiting.