hashicorp/terraform · error

failed to save file to %v: %v

Error message

failed to save file to %v: %v

What it means

Raised by putObject() when cosClient.Object.Put returns a nil response, meaning the HTTP request never produced a response object. The error wraps the underlying transport error. This path (rsp == nil) indicates the request failed before any COS response was received.

Source

Thrown at internal/backend/remote-state/cos/client.go:247

		ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{
			XCosMetaXXX: &http.Header{
				"X-Cos-Meta-Md5": []string{fmt.Sprintf("%x", md5.Sum(data))},
			},
		},
		ACLHeaderOptions: &cos.ACLHeaderOptions{
			XCosACL: c.acl,
		},
	}

	if c.encrypt {
		opt.ObjectPutHeaderOptions.XCosServerSideEncryption = "AES256"
	}

	r := bytes.NewReader(data)
	rsp, err := c.cosClient.Object.Put(c.cosContext, cosFile, r, opt)
	if rsp == nil {
		log.Printf("[DEBUG] putObject %s: error: %v", cosFile, err)
		return fmt.Errorf("failed to save file to %v: %v", cosFile, err)
	}
	defer rsp.Body.Close()

	log.Printf("[DEBUG] putObject %s: code: %d, error: %v", cosFile, rsp.StatusCode, err)
	if err != nil {
		return fmt.Errorf("failed to save file to %v: %v", cosFile, err)
	}

	return nil
}

// deleteObject delete remote object
func (c *remoteClient) deleteObject(cosFile string) error {
	rsp, err := c.cosClient.Object.Delete(c.cosContext, cosFile)
	if rsp == nil {
		log.Printf("[DEBUG] deleteObject %s: error: %v", cosFile, err)
		return fmt.Errorf("failed to delete file %v: %v", cosFile, err)
	}

View on GitHub (pinned to c9def3e214)

Solutions

  1. Verify network connectivity and DNS to the bucket endpoint: 'curl -I https://<bucket>.cos.<region>.myqcloud.com'.
  2. Confirm the bucket name and region in the backend block match the actual COS bucket.
  3. Check that the Tencent Cloud credentials (SecretId/SecretKey/SecurityToken) are valid and not expired, and that the COS/Tag STS scope covers object writes.
  4. Retry the apply — transient transport errors frequently clear on the next attempt.

Example fix

# verify endpoint reachability and credentials
curl -I https://tf-state-1234567890.cos.ap-guangzhou.myqcloud.com
# check region/bucket name in the backend block
terraform init -backend=false
Defensive patterns

Strategy: retry

Validate before calling

// Preflight: confirm the endpoint resolves and credentials work.
func cosReachable(ctx context.Context, endpoint string) error {
    req, _ := http.NewRequestWithContext(ctx, "HEAD", endpoint, nil)
    _, err := http.DefaultClient.Do(req)
    return err
}

Try / catch

if err := c.putObject(c.stateFile, data); err != nil {
    if strings.Contains(err.Error(), "failed to save file to") {
        // transport-level; safe to retry the Put
    }
}

Prevention

When it happens

Trigger: At client.go:244-247, the Put call returns (nil, err). Occurs on DNS resolution failure, connection refused, TLS handshake error, context cancellation before the request was sent, or the SDK's internal retry budget being exhausted with no successful response.

Common situations: Wrong region/endpoint in the backend config (bucket URL typo); network outage or firewall blocking egress to cos.*.myqcloud.com; expired/invalid SecretId/SecretKey causing auth-layer failure before a response; VPN/proxy dropping the connection; terraform killed (Ctrl-C) during the write.

Related errors


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