hashicorp/terraform · error
Key could not be found
Error message
Key %q could not be found
What it means
In RemoteClient.Get, when the state is stored in chunked mode, the manifest at the state path lists per-chunk KV paths. The client fetches each chunk; if any chunk returns a nil pair (key deleted or never written), this fires. It means the chunk index disagrees with Consul's KV store — the state is partially missing.
Solutions
- Inspect the manifest at the state key: `consul kv get <state-path>` and note the chunks array and current-hash.
- For each chunk path in the list, check `consul kv get <chunk>` to identify which are missing.
- Restore the missing chunks or the whole state prefix from a Consul snapshot taken when the state was consistent.
- If unrecoverable, restore a known-good state file and re-run terraform apply to reconcile.
Defensive patterns
Strategy: validation
Validate before calling
// Before relying on Get(), verify the chunk manifest agrees with KV.
func verifyChunksPresent(client *consulapi.Client, statePath string) error {
pair, _, err := client.KV().Get(statePath, nil)
if err != nil {
return err
}
if pair == nil {
return nil // not chunked or absent
}
var manifest struct {
CurrentHash string `json:"current-hash"`
Chunks []string `json:"chunks"`
}
if err := json.Unmarshal(pair.Value, &manifest); err != nil || manifest.CurrentHash == "" {
return nil // single-entry mode
}
for _, c := range manifest.Chunks {
p, _, err := client.KV().Get(c, nil)
if err != nil {
return fmt.Errorf("checking chunk %s: %w", c, err)
}
if p == nil {
return fmt.Errorf("missing chunk %s; state is corrupt", c)
}
}
return nil
} Prevention
- Take periodic Consul snapshots so a corrupt chunk set is recoverable.
- Never manually delete keys under the tfstate.<hash>/ chunk prefix.
- Avoid concurrent writers on the same state path.
- Monitor Consul KV for unexpected deletes on the state prefix.
When it happens
Trigger: RemoteClient.Get -> chunkedMode() returns chunked=true and a list of chunk paths; for one of them kv.Get(c) returns (nil, nil).
Common situations: Manual deletion of chunk keys under tfstate.<hash>/; a previous Put crashed between writing chunks and the manifest; another writer concurrently rewrote and trimmed chunks; Consul lost data after a bad snapshot restore.
Related errors
- error unmarshaling lock info
- The remote state does not match the expected hash
- consul CAS failed with transaction errors
- Error unlocking Consul state. Lock ID
- expected on 1 response value, got
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/4ec94156b8102bbc.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/consul/client.go:101
chunked, hash, chunks, pair, err := c.chunkedMode()
if err != nil {
return nil, diags.Append(err)
}
if pair == nil {
return nil, diags
}
c.modifyIndex = pair.ModifyIndex
var payload []byte
if chunked {
for _, c := range chunks {
pair, _, err := kv.Get(c, nil)
if err != nil {
return nil, diags.Append(err)
}
if pair == nil {
return nil, diags.Append(fmt.Errorf("Key %q could not be found", c))
}
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 {View on GitHub (pinned to d32a084675)