hashicorp/terraform · error

The remote state does not match the expected hash

Error message

The remote state does not match the expected hash

What it means

Raised by the Consul backend's RemoteClient.Get() (consul/client.go:120) after reassembling chunked state. In chunked mode the manifest key stores a 'current-hash'; on read Terraform recomputes the MD5 of the decompressed payload and compares it. A mismatch means the reassembled bytes do not correspond to the hash recorded when the chunks were written. It is the backend's integrity check that the KV tree was not partially rewritten or corrupted.

Source

Thrown at internal/backend/remote-state/consul/client.go:120

			}
			payload = append(payload, pair.Value[:]...)
		}
	} else {
		payload = pair.Value
	}

	// If the payload starts with 0x1f, it's gzip, not json
	if len(payload) >= 1 && payload[0] == '\x1f' {
		payload, err = uncompressState(payload)
		if err != nil {
			return nil, diags.Append(err)
		}
	}

	md5 := md5.Sum(payload)

	if hash != "" && fmt.Sprintf("%x", md5) != hash {
		return nil, diags.Append(fmt.Errorf("The remote state does not match the expected hash"))
	}

	return &remote.Payload{
		Data: payload,
		MD5:  md5[:],
	}, diags
}

func (c *RemoteClient) Put(data []byte) tfdiags.Diagnostics {
	// The state can be stored in 4 different ways, based on the payload size
	// and whether the user enabled gzip:
	//  - single entry mode with plain JSON: a single JSON is stored at
	//	  "tfstate/my_project"
	//  - single entry mode gzip: the JSON payload is first gziped and stored at
	//    "tfstate/my_project"
	//  - chunked mode with plain JSON: the JSON payload is split in pieces and
	//    stored like so:
	//       - "tfstate/my_project" -> a JSON payload that contains the path of

View on GitHub (pinned to c9def3e214)

Solutions

  1. Inspect the manifest KV at the configured path and verify every chunk path listed in its 'chunks' array still exists and is readable in Consul.
  2. If chunks are missing/damaged, restore from a known-good backup or a prior terraform state snapshot.
  3. Re-push a correct state with `terraform state push <file>` to overwrite the corrupted remote state and regenerate the manifest.
  4. Never hand-edit Consul chunk keys; always go through terraform so the hash and manifest stay consistent.

Example fix

// before: chunks were manually pruned in Consul, manifest hash now stale
// after: overwrite remote state atomically through terraform
terraform state push good_state.tfstate
Defensive patterns

Strategy: fallback

Try / catch

// On consul Get hash-mismatch, fall back to a trusted local/cached state copy
payload, diags := client.Get()
if diags.HasErrors() {
    if strings.Contains(diags.Err().Error(), "does not match the expected hash") {
        // do not trust the corrupted remote; use last known-good local state
        return useLocalFallback()
    }
    return diags
}

Prevention

When it happens

Trigger: Get() is called, chunkedMode() returns a non-empty hash, and fmt.Sprintf("%x", md5.Sum(payload)) != hash. This happens when one or more chunk keys under <path>/tfstate.<hash>/ are missing, reordered, or byte-modified relative to the manifest.

Common situations: Someone manually edited or deleted chunk KV entries in Consul; a previous Put was interrupted after writing some chunks but before updating the manifest; gzip setting changed between write and read so decompression yields different bytes; Consul storage corruption/disk loss.

Related errors


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/b68aaacba5a9f288. Report an issue: GitHub.