{"record":{"id":"cf3336eaef53bc54","repo":"hashicorp/packer","slug":"failed-to-write-download-w","errorCode":null,"errorMessage":"failed to write download: %w","messagePattern":"failed to write download: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"provisioner/hcp-sbom/packer_release_fetch.go","lineNumber":134,"sourceCode":"\tresp, err := client.Do(req)\n\tif err != nil {\n\t\t_ = f.Close()\n\t\t_ = os.Remove(tmpPath)\n\t\treturn \"\", fmt.Errorf(\"HTTP request failed: %w\", err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\t_ = f.Close()\n\t\t_ = os.Remove(tmpPath)\n\t\treturn \"\", fmt.Errorf(\"HTTP %d for %s\", resp.StatusCode, url)\n\t}\n\n\t_, copyErr := io.Copy(f, resp.Body)\n\tcloseErr := f.Close()\n\tif copyErr != nil {\n\t\t_ = os.Remove(tmpPath)\n\t\treturn \"\", fmt.Errorf(\"failed to write download: %w\", copyErr)\n\t}\n\tif closeErr != nil {\n\t\t_ = os.Remove(tmpPath)\n\t\treturn \"\", fmt.Errorf(\"failed to close temp file: %w\", closeErr)\n\t}\n\n\treturn tmpPath, nil\n}\n\n// downloadChecksumFile fetches the SHA256SUMS text file at url.\nfunc downloadChecksumFile(ctx context.Context, client *http.Client, url string) (string, error) {\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to build request for %s: %w\", url, err)\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/hashicorp/packer/blob/eb36e3c3e48a036f3e8cc94087636ee72e1303c9/provisioner/hcp-sbom/packer_release_fetch.go#L116-L152","documentation":"Once the server responds 200, downloadURLToTempFile streams resp.Body into the temp file with io.Copy. If the copy fails mid-transfer, the temp file is removed and this error wraps the underlying read/write failure. This catches incomplete or aborted downloads: the connection dropped partway through, the writer hit disk-full, or the request context was cancelled during streaming.","triggerScenarios":"io.Copy returns an error while streaming: network connection reset or timed out mid-body, disk fills while writing the zip, the context is cancelled during transfer, or an fs write error occurs on the temp file.","commonSituations":"Unstable Wi-Fi/VPN or flaky CI networking dropping large downloads; small temp-disk partitions exhausted by a multi-hundred-MB Packer zip; long downloads exceeding the 5-minute http.Client timeout mid-body; node preemption cancelling the context during transfer.","solutions":["Check the wrapped cause: 'connection reset by peer'/'context deadline exceeded' means retry (downloadPackerRelease retries automatically); 'no space left on device' means free temp-disk space.","Verify free space on the filesystem backing TMPDIR before large downloads (df -h $TMPDIR) and enlarge or relocate the temp directory.","Check network stability to releases.hashicorp.com; use a wired/stable connection or move the build agent closer to the CDN.","If downloads regularly exceed the 5-minute client timeout, raise the http.Client timeout or improve bandwidth rather than disabling it.","If the context can be cancelled upstream, give the download step an adequate deadline so transfers are not killed mid-stream."],"exampleFix":"// before: single attempt, dies on transient mid-body failure\npath, err := downloadURLToTempFile(ctx, client, zipURL, \".zip\")\n// after: ensure temp space and rely on bounded retries for transient copy errors\nif free, err := diskFree(os.TempDir()); err == nil && free < 1<<30 {\n    return errors.New(\"insufficient temp space (<1GiB) for Packer download\")\n}\nvar path string\nerr = retry.Config{Tries: 3, RetryDelay: func() time.Duration { return 5 * time.Second }}.\n    Run(ctx, func(ctx context.Context) error {\n        p, err := downloadURLToTempFile(ctx, client, zipURL, \".zip\")\n        path = p\n        return err\n    })","handlingStrategy":"retry","validationCode":"// Ensure enough temp space for the artifact before streaming it down\nfunc hasTempSpace(minBytes uint64) error {\n    var st syscall.Statfs_t\n    if err := syscall.Statfs(os.TempDir(), &st); err != nil {\n        return err\n    }\n    free := st.Bavail * uint64(st.Bsize)\n    if free < minBytes {\n        return fmt.Errorf(\"only %d bytes free in %s, need %d\", free, os.TempDir(), minBytes)\n    }\n    return nil\n}\n// usage: hasTempSpace(1 << 30) // require 1 GiB before downloading","typeGuard":null,"tryCatchPattern":"var path string\nerr := retry.Config{\n    Tries:      3,\n    RetryDelay: func() time.Duration { return 5 * time.Second },\n}.Run(ctx, func(ctx context.Context) error {\n    p, err := downloadURLToTempFile(ctx, client, url, \".zip\")\n    if err != nil {\n        if strings.Contains(err.Error(), \"no space left on device\") {\n            return retry.Fatal(err) // disk-full will not fix itself; stop retrying\n        }\n        return err // transient network/context errors: retry\n    }\n    path = p\n    return nil\n})","preventionTips":["Provision several GB of free space on the temp partition of build agents.","Give download steps a deadline longer than the worst-case transfer time at your link speed.","Avoid cancelling contexts mid-download; scope cancellation to phases, not the transfer itself.","Rerun on transient 'connection reset' errors — the caller's retry loop handles them.","Verify downloaded size/checksum (as the caller does with SHA256SUMS) to catch truncated transfers that do not error."],"tags":["network","filesystem","disk-space","download","io"],"backgroundTag":"download-write-failed","analyzedSha":"eb36e3c3e48a036f3e8cc94087636ee72e1303c9","analyzedAt":"2026-09-05T13:20:43.127Z","contentChangedAt":"2026-09-05T13:20:43.127Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}