TerraformTerraform

Error: Inconsistent dependency lock file

The lock file does not record a provider your configuration needs, or lacks a hash for the platform running the plan. Why terraform init -upgrade is usually the wrong fix in CI.

medium fix6 min read

the terraform error
Error: Inconsistent dependency lock file
The following dependency selections recorded in the lock file are inconsistent with the current configuration:
  - provider registry.terraform.io/hashicorp/random: required by this configuration but no version is selected

Error: Failed to install provider
Provider registry.terraform.io/hashicorp/aws v5.70.0 does not have a package available for your current platform, linux_arm64.

Error: Required plugins are not installed

Do this first3 steps

Run these in order. Each one tells you what its output means before you change anything.

  1. 1

    See which provider the lock file is missing

    terraform providers

    This prints every provider the configuration requires, including ones pulled in by modules. Compare that list against the provider blocks in .terraform.lock.hcl to find what is absent.

  2. 2

    Record the missing provider without upgrading everything else

    terraform init

    A plain init adds a missing provider to the lock file at the newest version allowed by your constraints, and leaves every already-locked provider alone. Reach for -upgrade only when you intend to move versions.

  3. 3

    If CI runs a different architecture, lock hashes for every platform

    terraform providers lock -platform=linux_amd64 -platform=linux_arm64 -platform=darwin_arm64

    The lock file stores per-platform checksums. A file generated only on an Apple Silicon Mac has no hashes for a linux_amd64 runner, which fails the verification in CI even though the version is correct.

All 8 sections

.terraform.lock.hcl records which provider versions were selected and their checksums. Terraform refuses to proceed when what the configuration needs and what the lock file records disagree.

Two variants, with different fixes.

Variant 1: A provider is required but not locked

- provider registry.terraform.io/hashicorp/random: required by this
  configuration but no version is selected

Something now uses a provider the lock file has never seen. Usually because you added a resource, or a module you upgraded started using one.

terraform providers
Providers required by configuration:
.
├── provider[registry.terraform.io/hashicorp/aws] ~> 5.0
├── provider[registry.terraform.io/hashicorp/random]
└── module.vpc
    └── provider[registry.terraform.io/hashicorp/aws] >= 4.0

Note it includes providers required by modules. A module bumped from 3.x to 4.x may add one without any change to your own code, which is why this appears after a module upgrade you thought was unrelated.

The fix is a plain init:

terraform init

This adds the missing provider at the newest version your constraints allow and leaves the others alone. Then commit the updated lock file.

Why not -upgrade

terraform init -upgrade also works, and it does more than you asked: it re-resolves every provider to the newest allowed version. With ~> 5.0 that can move AWS from 5.31 to 5.70 as a side effect of adding random.

That is fine when you meant to upgrade. It is not fine as a reflex fix in CI, where it silently defeats the entire purpose of a lock file: two runs a week apart get different provider versions, and a provider bug arrives in a pipeline nobody changed.

If a pipeline needs -upgrade to work, the lock file is not committed, or it is out of date and should be updated deliberately in a reviewed change.

Variant 2: No hash for this platform

Provider registry.terraform.io/hashicorp/aws v5.70.0 does not have a
package available for your current platform, linux_arm64.

The lock file stores checksums per platform:

provider "registry.terraform.io/hashicorp/aws" {
  version     = "5.70.0"
  constraints = "~> 5.0"
  hashes = [
    "h1:xxxxx...",
    "zh:aaaa...",
  ]
}

Generate it on an Apple Silicon Mac and it holds darwin_arm64 hashes. A linux_amd64 CI runner then finds no hash it can verify and fails, even though the version is right.

Record every platform you build on:

terraform providers lock \
  -platform=linux_amd64 \
  -platform=linux_arm64 \
  -platform=darwin_arm64 \
  -platform=darwin_amd64 \
  -platform=windows_amd64

Commit the result. Run it again whenever you change provider versions or add a platform, such as moving CI to Graviton runners.

In CI, use -lockfile=readonly

This is the setting that makes the lock file mean something:

terraform init -lockfile=readonly

The job fails if the lock file would need to change, rather than quietly changing it. A dependency drift then surfaces as a failed build with a clear cause, instead of an apply using a provider version nobody reviewed.

# GitLab CI
plan:
  script:
    - terraform init -lockfile=readonly
    - terraform plan -out=tfplan

Pair it with a pinned Terraform version. The lock file format is forward-compatible but not backward-compatible, so a newer CLI writing it locally can produce a file an older CLI in CI cannot read.

Provider version constraints

The lock file records what was chosen; required_providers constrains what may be chosen.

terraform {
  required_version = "~> 1.9.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.70"
    }
  }
}

~> 5.70 allows 5.70 and above within 5.x. ~> 5.70.0 allows only 5.70.x. Neither pins exactly; the lock file does that, which is why both are needed.

A module with a conflicting constraint produces a different error naming both:

Error: Incompatible provider requirements

Resolve that by widening one constraint or upgrading the module, not by deleting the lock file.

When to just delete it

Rarely, and never in CI. If the lock file is genuinely corrupt:

rm .terraform.lock.hcl
rm -rf .terraform
terraform init
terraform providers lock -platform=linux_amd64 -platform=darwin_arm64

This re-resolves everything to the newest allowed versions, so review the diff before committing. Treat it as a deliberate upgrade and test it, not as a repair.

A checklist

  1. Read which variant: a missing provider, or a missing platform hash.
  2. terraform providers to see what the configuration actually requires.
  3. Missing provider → plain terraform init, then commit the lock file.
  4. Avoid -upgrade unless you mean to move versions.
  5. Platform error → terraform providers lock -platform=... for every platform you use.
  6. Commit .terraform.lock.hcl. It is not a cache.
  7. CI → terraform init -lockfile=readonly.
  8. Pin the Terraform version so local and CI write the same format.

Frequently Asked Questions

Should I commit .terraform.lock.hcl to version control?

Yes. It is a lock file in the same sense as package-lock.json or Cargo.lock: it records the exact provider versions and checksums that were verified to work, so every machine and every pipeline run resolves identically. Without it, each terraform init picks the newest version your constraints allow, and a provider released overnight can change a plan in a repository nobody touched. The .terraform/ directory alongside it is a cache and should stay ignored; the lock file should not.

What is the difference between terraform init and terraform init -upgrade?

Plain init keeps every provider already recorded in the lock file at its locked version and only resolves ones that are missing. -upgrade re-resolves all of them to the newest version each constraint permits. So adding a new provider needs a plain init, while -upgrade is a deliberate act of moving versions that should produce a reviewable diff in the lock file. Using -upgrade routinely in CI defeats the lock file entirely, since two runs a week apart can then use different providers.

Why does the lock file work locally but fail in CI?

Because the checksums are recorded per platform. A lock file generated on an Apple Silicon Mac contains darwin_arm64 hashes, and a linux_amd64 runner finds no hash it can verify, so verification fails even though the version is correct. Run terraform providers lock with a -platform flag for every operating system and architecture you build on, then commit the result. You need to repeat it whenever provider versions change or you add a platform, such as switching CI to ARM runners.

What does -lockfile=readonly do?

It makes terraform init fail rather than modify the lock file. That turns any unrecorded dependency change into an explicit build failure instead of a silent update, which is what you want in a pipeline: the lock file should only change through a reviewed commit. Without it, CI happily writes a new lock file inside the job, uses whatever it resolved, and discards the change when the runner is torn down, so the version that was applied is not the version anyone approved.

Is it safe to delete the lock file to fix an error?

It resolves most lock file errors and it is a blunt instrument, because it re-resolves every provider to the newest permitted version. That is an upgrade, not a repair, and it should be treated as one: review the resulting diff, run a plan, and test before merging. Never do it inside CI, where it removes the guarantee that the pipeline is using reviewed versions. Prefer the targeted fixes first, a plain init for a missing provider and terraform providers lock -platform=... for a missing hash.

Reference and practice

Learn the underlying concept

Other Terraform errors