hashicorp/terraform · error
failed to open file at
Error message
failed to open file at %v: checksum %s invalid
What it means
Thrown by getObject() when the returned object has no valid MD5 checksum metadata. The COS backend stores a 32-char hex MD5 in the `X-Cos-Meta-Md5` header on every put; on GET it requires exactly that header with length 32. A missing/short/long header means the object was not written by this backend (or was corrupted/rewritten externally).
Solutions
- Confirm the object was originally created by this COS backend (check for X-Cos-Meta-Md5 in the object's metadata via the COS console/API).
- If the object is the genuine state but lacks metadata, re-upload it with the custom header set to the hex MD5 of its bytes, or push the state fresh via `terraform state push`.
- If the object is stale/foreign, delete it and let Terraform write the state correctly on the next apply.
- Avoid manual uploads to the state key path; always use the backend's own Put path so metadata is preserved.
Example fix
// before: state uploaded manually, missing X-Cos-Meta-Md5 // -> getObject fails: checksum invalid // after: push state through terraform so the header is written // terraform state push /path/to/terraform.tfstate // (or set the header explicitly when uploading out of band) // x-cos-meta-md5: <lowercase 32-char hex md5 of the file>
Defensive patterns
Strategy: validation
Validate before calling
// Before relying on a state object, verify it carries the COS backend metadata
func hasBackendMetadata(ctx context.Context, client *cos.Client, key string) error {
rsp, err := client.Object.Head(ctx, key, nil)
if err != nil || rsp == nil { return err }
defer rsp.Body.Close()
md5 := rsp.Header.Get("X-Cos-Meta-Md5")
if len(md5) != 32 {
return fmt.Errorf("object %s missing valid X-Cos-Meta-Md5 (got %q); re-upload via terraform state push", key, md5)
}
return nil
} Type guard
func isBackendWrittenObject(headers http.Header) bool {
return len(headers.Get("X-Cos-Meta-Md5")) == 32
} Try / catch
// If getObject fails with the checksum-invalid message, route the user to
// re-push the state instead of retrying blindly:
if strings.Contains(err.Error(), "checksum") && strings.Contains(err.Error(), "invalid") {
return fmt.Errorf("state object lacks backend metadata; run `terraform state push <file>`: %w", err)
} Prevention
- Never upload state files to COS out of band; always use `terraform state push` or the backend itself.
- If copying objects between buckets, preserve custom metadata (use COS copy-with-metadata).
- After migrations, run a read-only `terraform state pull` to confirm the backend can read the new object.
- Avoid lifecycle rules that rewrite objects in a way that strips custom headers.
When it happens
Trigger: Object.Get succeeds (200, no SDK error) but rsp.Header.Get("X-Cos-Meta-Md5") returns empty or non-32-char string. Happens when the state object was uploaded by the GCS/S3 console, a different tool, a `coscp` copy without metadata, or a prior Terraform version/bug that omitted the header.
Common situations: Migrating state by manually uploading `terraform.tfstate` to COS without setting the custom metadata; a third-party sync tool rewrote the object and stripped user metadata; bucket lifecycle rule transitioned the object in a way that dropped custom headers.
Related errors
- failed to open file at
- bucket not exists
- failed to create bucket
- failed to create tag
- failed to delete bucket
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/55936ae3b16545a1.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/cos/client.go:204
err = fmt.Errorf("failed to open file at %v: %v", cosFile, err)
return
}
defer rsp.Body.Close()
log.Printf("[DEBUG] getObject %s: code: %d, error: %v", cosFile, rsp.StatusCode, err)
if err != nil {
if rsp.StatusCode == 404 {
err = nil
} else {
err = fmt.Errorf("failed to open file at %v: %v", cosFile, err)
}
return
}
checksum = rsp.Header.Get("X-Cos-Meta-Md5")
log.Printf("[DEBUG] getObject %s: checksum: %s", cosFile, checksum)
if len(checksum) != 32 {
err = fmt.Errorf("failed to open file at %v: checksum %s invalid", cosFile, checksum)
return
}
exists = true
data, err = ioutil.ReadAll(rsp.Body)
log.Printf("[DEBUG] getObject %s: data length: %d", cosFile, len(data))
if err != nil {
err = fmt.Errorf("failed to open file at %v: %v", cosFile, err)
return
}
check := fmt.Sprintf("%x", md5.Sum(data))
log.Printf("[DEBUG] getObject %s: check: %s", cosFile, check)
if check != checksum {
err = fmt.Errorf("failed to open file at %v: checksum mismatch, %s != %s", cosFile, check, checksum)
return
}
View on GitHub (pinned to d32a084675)