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

  1. 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).
  2. 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`.
  3. If the object is stale/foreign, delete it and let Terraform write the state correctly on the next apply.
  4. 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

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


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)