hashicorp/packer · error
failed to write download: %w
Error message
failed to write download: %w
What it means
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.
Source
Thrown at provisioner/hcp-sbom/packer_release_fetch.go:134
resp, err := client.Do(req)
if err != nil {
_ = f.Close()
_ = os.Remove(tmpPath)
return "", fmt.Errorf("HTTP request failed: %w", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
_ = f.Close()
_ = os.Remove(tmpPath)
return "", fmt.Errorf("HTTP %d for %s", resp.StatusCode, url)
}
_, copyErr := io.Copy(f, resp.Body)
closeErr := f.Close()
if copyErr != nil {
_ = os.Remove(tmpPath)
return "", fmt.Errorf("failed to write download: %w", copyErr)
}
if closeErr != nil {
_ = os.Remove(tmpPath)
return "", fmt.Errorf("failed to close temp file: %w", closeErr)
}
return tmpPath, nil
}
// downloadChecksumFile fetches the SHA256SUMS text file at url.
func downloadChecksumFile(ctx context.Context, client *http.Client, url string) (string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return "", fmt.Errorf("failed to build request for %s: %w", url, err)
}
resp, err := client.Do(req)
if err != nil {View on GitHub (pinned to eb36e3c3e4)
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.
Example fix
// before: single attempt, dies on transient mid-body failure
path, err := downloadURLToTempFile(ctx, client, zipURL, ".zip")
// after: ensure temp space and rely on bounded retries for transient copy errors
if free, err := diskFree(os.TempDir()); err == nil && free < 1<<30 {
return errors.New("insufficient temp space (<1GiB) for Packer download")
}
var path string
err = retry.Config{Tries: 3, RetryDelay: func() time.Duration { return 5 * time.Second }}.
Run(ctx, func(ctx context.Context) error {
p, err := downloadURLToTempFile(ctx, client, zipURL, ".zip")
path = p
return err
}) Defensive patterns
Strategy: retry
Validate before calling
// Ensure enough temp space for the artifact before streaming it down
func hasTempSpace(minBytes uint64) error {
var st syscall.Statfs_t
if err := syscall.Statfs(os.TempDir(), &st); err != nil {
return err
}
free := st.Bavail * uint64(st.Bsize)
if free < minBytes {
return fmt.Errorf("only %d bytes free in %s, need %d", free, os.TempDir(), minBytes)
}
return nil
}
// usage: hasTempSpace(1 << 30) // require 1 GiB before downloading Try / catch
var path string
err := retry.Config{
Tries: 3,
RetryDelay: func() time.Duration { return 5 * time.Second },
}.Run(ctx, func(ctx context.Context) error {
p, err := downloadURLToTempFile(ctx, client, url, ".zip")
if err != nil {
if strings.Contains(err.Error(), "no space left on device") {
return retry.Fatal(err) // disk-full will not fix itself; stop retrying
}
return err // transient network/context errors: retry
}
path = p
return nil
}) Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to download Packer release zip: %w
- failed to open %s: %s
- failed to read %s: %s
- read attestation %q: %w
- failed to get source: %w
AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05).
Data as JSON: /api/errors/cf3336eaef53bc54.
Report an issue: GitHub.