Initializing Enclave...

How to Fix Terraform 'Failed to Install Provider' Checksum Mismatch Error

Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5–15 mins

TL;DR

  • What broke: Terraform's SHA256 hash for the downloaded provider binary doesn't match the hash recorded in .terraform.lock.hcl or the upstream registry signature, so terraform init hard-aborts.
  • How to fix it: Delete the stale lock file entry or the entire .terraform.lock.hcl, run terraform providers lock with explicit platform targets to regenerate correct multi-platform hashes, then re-run terraform init.
  • Use our Client-Side Sandbox below to paste your lock file and error output — it will auto-generate the corrected lock file diff and the exact terraform providers lock command for your platform matrix.

The Incident (What Does the Error Mean?)

Raw error output from a failing terraform init:

Error: Failed to install provider

Error while installing hashicorp/aws v5.31.0: the current package for
registry.terraform.io/hashicorp/aws 5.31.0 doesn't match any of the
checksum values previously recorded in the dependency lock file.

For more context, see:
  https://www.terraform.io/language/files/dependency-lock

Or the variant thrown when a cached zip is corrupt:

Error: Failed to install provider from shared cache

Error while installing hashicorp/google v5.10.0 from the shared cache
directory: the provider package in the cache at
.terraform/providers/.../google_5.10.0_linux_amd64.zip
has a hash that doesn't match any recorded in the lock file:
  got:  zh:8abc1234...
  want: zh:deadbeef...

Immediate consequence: terraform init exits non-zero. No plan, no apply, no pipeline. Every downstream job — PR validation, scheduled drift detection, emergency hotfixes — is dead until this is resolved.


The Attack Vector / Blast Radius

This error has two completely different causes with wildly different blast radii. Conflating them is how teams waste hours.

Cause A — Benign: Stale or platform-incomplete lock file (95% of cases)

Your .terraform.lock.hcl was committed on macOS (darwin_amd64) but CI runs on linux_amd64. The lock file only contains h1: hashes for the Darwin zip. When the Linux runner downloads the Linux zip, its hash is absent from the lock file. Terraform refuses to proceed. This is a workflow problem, not a security incident.

Cause B — Critical: Supply-chain substitution or mirror tampering (5% of cases, treat as P0)

If the hash mismatch occurs on the same platform that originally generated the lock file, and you have not changed the provider version, this is a red flag for:

  • A compromised internal Artifactory/Nexus mirror serving a modified binary.
  • A DNS hijack redirecting registry.terraform.io to a malicious mirror.
  • A CI runner with a poisoned shared provider cache (.terraform/providers/ or TF_PLUGIN_CACHE_DIR).

A tampered provider binary executes with full credentials available to Terraform — your AWS keys, GCP service account, Vault tokens. It can exfiltrate credentials, create backdoor IAM users, or destroy resources. The blast radius is your entire cloud estate.

Before you run any fix: verify the expected hash against the official registry.

# Cross-check the legitimate SHA256 from HashiCorp's registry API
curl -s https://registry.terraform.io/v1/providers/hashicorp/aws/5.31.0/download/linux/amd64 \
  | python3 -m json.tool | grep shasum

If the hash the registry returns matches what Terraform downloaded but not what's in your lock file → Cause A. If the hash Terraform downloaded does not match the registry → treat as a security incident, rotate all credentials, and audit your network path.


How to Fix It (The Solution)

Basic Fix — Regenerate the Lock File

Delete the stale lock file and let Terraform rebuild it with hashes for all required platforms in one shot.

# Step 1: Remove the lock file (or just the offending provider block inside it)
rm .terraform.lock.hcl

# Step 2: Regenerate with explicit platform targets
# List every platform your team and CI use
terraform providers lock \
  -platform=linux_amd64 \
  -platform=linux_arm64 \
  -platform=darwin_amd64 \
  -platform=darwin_arm64 \
  -platform=windows_amd64

# Step 3: Commit the regenerated lock file
git add .terraform.lock.hcl
git commit -m "fix: regenerate provider lock file with multi-platform hashes"

# Step 4: Re-run init
terraform init

If you only need to fix a single provider without nuking the whole lock file:

terraform providers lock \
  -platform=linux_amd64 \
  -platform=darwin_arm64 \
  registry.terraform.io/hashicorp/aws

Enterprise Best Practice — Pin Providers + Use a Verified Internal Mirror

The root cause of Cause A is almost always an unpinned provider version combined with a lock file committed from a single platform. The root cause of Cause B is trusting an unverified mirror.

Bad versions.tf (causes drift and platform hash gaps):

- terraform {
-   required_providers {
-     aws = {
-       source  = "hashicorp/aws"
-       version = "~> 5.0"  # too loose; any 5.x can be pulled
-     }
-   }
- }

Good versions.tf (exact pin + explicit registry):

+ terraform {
+   required_providers {
+     aws = {
+       source  = "registry.terraform.io/hashicorp/aws"
+       version = "= 5.31.0"  # exact pin; lock file stays stable
+     }
+   }
+   required_version = ">= 1.6.0"
+ }

Bad CI workflow (no cache validation, single-platform lock file):

- - name: Terraform Init
-   run: terraform init
-   # Lock file committed from dev laptop, CI is linux_amd64 → mismatch every time

Good CI workflow (validates lock file is not dirty post-init):

+ - name: Terraform Providers Lock (multi-platform)
+   run: |
+     terraform providers lock \
+       -platform=linux_amd64 \
+       -platform=darwin_arm64
+     git diff --exit-code .terraform.lock.hcl || \
+       (echo "ERROR: lock file is stale, commit the updated .terraform.lock.hcl" && exit 1)
+
+ - name: Terraform Init
+   run: terraform init -backend=false
+   env:
+     TF_PLUGIN_CACHE_DIR: ${{ runner.tool_cache }}/terraform-plugins

If using a private mirror (Artifactory, Nexus, S3-backed), enforce signature verification in ~/.terraformrc:

- # No mirror config — Terraform hits public registry directly
+ provider_installation {
+   network_mirror {
+     url     = "https://terraform-mirror.internal.corp/providers/"
+     include = ["registry.terraform.io/hashicorp/*"]
+   }
+   # Fallback to official registry for everything else
+   direct {
+     exclude = ["registry.terraform.io/hashicorp/*"]
+   }
+ }

Ensure your mirror proxies the upstream terraform-provider-*.zip files and the accompanying .sha256sums + .sha256sums.sig files. Terraform verifies the GPG signature on the shasums file — if your mirror strips these, you're running unsigned binaries.


💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.


Prevention in CI/CD

1. Enforce lock file freshness with a pre-commit hook

# .git/hooks/pre-commit (or use pre-commit framework)
#!/bin/bash
if git diff --cached --name-only | grep -q 'versions.tf\|\.tf$'; then
  terraform providers lock \
    -platform=linux_amd64 \
    -platform=darwin_arm64 \
    2>/dev/null
  git add .terraform.lock.hcl
fi

2. Checkov policy to block unpinned providers

checkov -d . --check CKV_TF_1  # Ensures all modules use pinned hashes

3. OPA/Conftest policy to enforce exact version pinning

# policy/terraform_provider_pin.rego
package terraform.providers

deny[msg] {
  provider := input.configuration.provider_config[_]
  not regex.match(`^= \d+\.\d+\.\d+$`, provider.version_constraint)
  msg := sprintf(
    "Provider '%v' must use an exact version pin (= X.Y.Z), got: %v",
    [provider.name, provider.version_constraint]
  )
}
# Run in CI
terraform show -json tfplan.json | conftest test - --policy policy/

4. Dependabot / Renovate for automated provider version bumps

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: terraform
    directory: "/"
    schedule:
      interval: weekly
    # Renovate: set rangeStrategy to "pin" to force exact pins

5. Verify provider GPG signatures in your pipeline (defense against Cause B)

# Download and verify the shasums signature before terraform init
PROVIDER_VERSION="5.31.0"
OS="linux"
ARCH="amd64"
BASE_URL="https://releases.hashicorp.com/terraform-provider-aws/${PROVIDER_VERSION}"

curl -sO "${BASE_URL}/terraform-provider-aws_${PROVIDER_VERSION}_SHA256SUMS"
curl -sO "${BASE_URL}/terraform-provider-aws_${PROVIDER_VERSION}_SHA256SUMS.sig"

# Import HashiCorp's signing key (do this once in your base image)
gpg --recv-keys 34365D9472D7468F

# Verify
gpg --verify \
  "terraform-provider-aws_${PROVIDER_VERSION}_SHA256SUMS.sig" \
  "terraform-provider-aws_${PROVIDER_VERSION}_SHA256SUMS" \
  && echo "Signature VALID" || (echo "SIGNATURE INVALID — POSSIBLE SUPPLY CHAIN ATTACK" && exit 1)

Run this check before terraform init in any pipeline that touches production. A failed GPG verification is a security incident, not a config error.

Related Diagnostics

"Part of the Security Utility Matrix."

View all 140 Security Tools →