hashicorp/terraform · error
failed to store state MD5
Error message
failed to store state MD5: %s
What it means
Thrown by RemoteClient.Put after a successful OSS PutObject: writing the state's MD5 digest to Tablestore (c.putMD5) failed. Because the next Get validates against this digest, the backend aborts the whole Put rather than leave an unverifiable state.
Solutions
- Confirm the OTS table configured for the backend still exists and credentials have ots:PutRow.
- Verify OTS endpoint reachability from the runner.
- Re-run terraform apply once OTS is healthy — OSS already has the new state, only the digest needs to catch up.
- If MD5 row conflicts, ensure the state key path is unique per workspace.
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: confirm the OTS table accepts PutRow for the MD5 primary key.
if _, err := c.otsClient.PutRow(md5Probe); err != nil {
return fmt.Errorf("OTS MD5 write pre-flight failed: %w", err)
} Try / catch
// If MD5 write fails after OSS PutObject, retry the MD5 write a few times before aborting.
for i := 0; i < 3; i++ {
if err := c.putMD5(sum[:]); err == nil { return diags }
time.Sleep(backoff); backoff *= 2
}
return diags.Append(fmt.Errorf("failed to store state MD5: %s", err)) Prevention
- Ensure OTS and OSS credentials/permissions are provisioned together.
- Monitor OTS health and alert on PutRow failures.
- Keep OTS table provisioned for write throughput during applies.
When it happens
Trigger: putMD5 (an OTS PutRow on the MD5/lock table) errored after the OSS object was written — OTS endpoint unreachable, table missing, or credentials lacking ots:PutRow.
Common situations: OTS table deleted or renamed mid-run; RAM permissions for OTS narrower than OSS; OTS regional outage; the MD5 row primary key conflicts with an existing row from a different state path.
Related errors
- state data in OSS does not have the expected content. This…
- error describing table store
- failed to retrieve lock info
- Error unlocking Alibaba Cloud OSS state file: Lock ID
- failed to lock OSS state
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/57c5be3aee7e3e98.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/oss/client.go:129
options = append(options, oss.ACL(oss.ACLType(c.acl)))
}
options = append(options, oss.ContentType("application/json"))
if c.serverSideEncryption {
options = append(options, oss.ServerSideEncryption("AES256"))
}
options = append(options, oss.ContentLength(int64(len(data))))
if body != nil {
if err := bucket.PutObject(c.stateFile, body, options...); err != nil {
return diags.Append(fmt.Errorf("failed to upload state %s: %#v", c.stateFile, err))
}
}
sum := md5.Sum(data)
if err := c.putMD5(sum[:]); err != nil {
// if this errors out, we unfortunately have to error out altogether,
// since the next Get will inevitably fail.
return diags.Append(fmt.Errorf("failed to store state MD5: %s", err))
}
return diags
}
func (c *RemoteClient) Delete() tfdiags.Diagnostics {
var diags tfdiags.Diagnostics
bucket, err := c.ossClient.Bucket(c.bucketName)
if err != nil {
return diags.Append(fmt.Errorf("error getting bucket %s: %#v", c.bucketName, err))
}
log.Printf("[DEBUG] Deleting remote state from OSS: %#v", c.stateFile)
if err := bucket.DeleteObject(c.stateFile); err != nil {
return diags.Append(fmt.Errorf("error deleting state %s: %#v", c.stateFile, err))
}
if err := c.deleteMD5(); err != nil {View on GitHub (pinned to d32a084675)