Skip to content
daniel@cosenza:~/blog
SRE & DevOpsFix July 5, 2026 3 min read

Fixing Terraform Provider Version Conflicts

terraform init fails with incompatible provider version constraints, or plan produces unexpected changes after an update. Here's how to read the constraint error and pin versions correctly across a team.

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

provider "aws" {
  # too permissive — any 5.x version satisfies this
  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

provider "aws" {
  version = "= 5.31.0"
}

Pinning exactly, rather than allowing a range, trades automatic minor-version updates for predictability — the right trade-off for most production infrastructure code.

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.

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.

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.