{"record":{"id":"f10d4d46c0357cb4","repo":"opentofu/opentofu","slug":"failed-to-save-file-to-v-w","errorCode":null,"errorMessage":"failed to save file to %v: %w","messagePattern":"failed to save file to (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/backend/remote-state/cos/client.go","lineNumber":253,"sourceCode":"\t\tObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{\n\t\t\tXCosMetaXXX: &http.Header{\n\t\t\t\t\"X-Cos-Meta-Md5\": []string{fmt.Sprintf(\"%x\", md5.Sum(data))},\n\t\t\t},\n\t\t},\n\t\tACLHeaderOptions: &cos.ACLHeaderOptions{\n\t\t\tXCosACL: c.acl,\n\t\t},\n\t}\n\n\tif c.encrypt {\n\t\topt.ObjectPutHeaderOptions.XCosServerSideEncryption = \"AES256\"\n\t}\n\n\tr := bytes.NewReader(data)\n\trsp, err := c.cosClient.Object.Put(ctx, cosFile, r, opt)\n\tif rsp == nil {\n\t\tlog.Printf(\"[DEBUG] putObject %s: error: %v\", cosFile, err)\n\t\treturn fmt.Errorf(\"failed to save file to %v: %w\", cosFile, err)\n\t}\n\tdefer rsp.Body.Close()\n\n\tlog.Printf(\"[DEBUG] putObject %s: code: %d, error: %v\", cosFile, rsp.StatusCode, err)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to save file to %v: %w\", cosFile, err)\n\t}\n\n\treturn nil\n}\n\n// deleteObject delete remote object\nfunc (c *remoteClient) deleteObject(ctx context.Context, cosFile string) error {\n\trsp, err := c.cosClient.Object.Delete(ctx, cosFile)\n\tif rsp == nil {\n\t\tlog.Printf(\"[DEBUG] deleteObject %s: error: %v\", cosFile, err)\n\t\treturn fmt.Errorf(\"failed to delete file %v: %w\", cosFile, err)\n\t}","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/opentofu/opentofu/blob/3561785c48c1ce615e7c50261bd351f26053efa2/internal/backend/remote-state/cos/client.go#L235-L271","documentation":"putObject got a nil response from Object.Put: the request never completed at the HTTP level, so the state (or lock file) was not persisted and the raw transport error is wrapped with the target path. Any tofu operation that writes state (apply persistence, state push, lock creation) can hit this. A failed Put does not damage the previous state object.","triggerScenarios":"DNS/TLS failure reaching cos.<region>.myqcloud.com; connection reset during upload; context canceled mid-Put; proxy rejecting PUT requests; SDK failing before send due to broken configuration.","commonSituations":"VPN drops during long applies; CI egress filters that allow GET but block PUT; very large states timing out at intermediary proxies.","solutions":["Inspect the wrapped error for dial/TLS/context causes","Confirm the bucket endpoint is reachable from the runner (curl the bucket URL)","Fix network/proxy and retry the operation — the prior state object remains intact","For recurring timeouts on large states, raise client/proxy limits or reduce state size"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isTransportError(err error) bool {\n\tvar nerr net.Error\n\tif errors.As(err, &nerr) {\n\t\treturn true\n\t}\n\treturn strings.Contains(err.Error(), \"dial tcp\") ||\n\t\tstrings.Contains(err.Error(), \"TLS handshake\")\n}","tryCatchPattern":"for i := 0; i < 3; i++ {\n\tif err := c.Put(ctx, data); err == nil {\n\t\tbreak\n\t} else if isTransportError(err) {\n\t\ttime.Sleep(time.Duration(i+1) * 2 * time.Second)\n\t\tcontinue\n\t} else {\n\t\treturn err\n\t}\n}","preventionTips":["Check PUT reachability through the proxy/egress firewall before CI runs","A failed Put leaves the previous state intact — safe to retry the whole command","Keep states lean to reduce upload windows on unstable links"],"tags":["go","tencent-cloud","cos","network","state","remote-state"],"backgroundTag":null,"analyzedSha":"3561785c48c1ce615e7c50261bd351f26053efa2","analyzedAt":"2026-08-15T23:27:16.226Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}