hashicorp/terraform · error
failed to save file to
Error message
failed to save file to %v: %v
What it means
Returned by putObject() when the COS SDK returns a nil response pointer from Object.Put. Identical in shape to error 201 but on the write path: no HTTP response was ever received, indicating a transport-level failure to reach COS for the PUT. The original SDK error is wrapped.
Solutions
- Inspect the wrapped error and the DEBUG line `putObject <file>: error:`.
- Verify TENCENTCLOUD_SECRETID/SECRETKEY and the bucket region/endpoint.
- Test outbound HTTPS connectivity from the host to the COS endpoint.
- Retry once transient issues are excluded; for large states, ensure no middlebox is truncating PUT bodies.
Example fix
// before: missing region, PUT cannot be signed/sent
backend "cos" { bucket="tf-state"; secret_id="..."; secret_key="..." }
// after: include region
backend "cos" { bucket="tf-state"; region="ap-shanghai"; secret_id="..."; secret_key="..." } Defensive patterns
Strategy: retry
Validate before calling
// Validate write-path connectivity before applying
func canWriteObject(ctx context.Context, client *cos.Client, key string) error {
// probe with a HEAD on the bucket; for create semantics, attempt a tiny PUT to a probe key
rsp, err := client.Object.Put(ctx, key+".probe", bytes.NewReader([]byte("x")), nil)
if rsp == nil { return fmt.Errorf("transport failure writing to COS: %w", err) }
rsp.Body.Close()
if rsp.StatusCode >= 400 { return fmt.Errorf("write probe failed: %d", rsp.StatusCode) }
client.Object.Delete(ctx, key+".probe")
return nil
} Type guard
func isNilResponse(rsp *cos.Response) bool { return rsp == nil } Try / catch
// On PUT with nil response, retry with exponential backoff (transport-level):
backoff.Retry(func() error {
rsp, err := client.Object.Put(ctx, key, r, opt)
if rsp == nil { return err } // retryable
defer rsp.Body.Close()
if err != nil { return backoff.Permanent(err) }
return nil
}, backoff.NewExponentialBackOff()) Prevention
- Run Terraform from hosts with reliable outbound HTTPS to COS.
- Set sensible HTTP timeouts and retries on the COS SDK client for write paths.
- Validate credentials and region before `terraform apply`.
- For large states, ensure no middlebox truncates PUT bodies.
When it happens
Trigger: c.cosClient.Object.Put(...) returns (nil, err) — network unreachable, invalid endpoint, DNS failure, TLS handshake failure, credentials missing so the SDK cannot sign, or context cancellation before the request completed.
Common situations: Backend configured without a valid `region`/`endpoint`; rotated Tencent Cloud keys not refreshed in env/backend; running Terraform in a sandboxed CI runner without outbound access to `cos.<region>.myqcloud.com`; large state PUT exceeding a network middlebox limit and the connection being reset.
Related errors
- failed to create bucket
- failed to delete bucket
- failed to delete file
- failed to open file at
- bucket not exists
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/615d3e15ad41daf0.
Report an issue: GitHub.
Appendix: 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 d32a084675)