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

  1. 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.
  2. Verify free space on the filesystem backing TMPDIR before large downloads (df -h $TMPDIR) and enlarge or relocate the temp directory.
  3. Check network stability to releases.hashicorp.com; use a wired/stable connection or move the build agent closer to the CDN.
  4. If downloads regularly exceed the 5-minute client timeout, raise the http.Client timeout or improve bandwidth rather than disabling it.
  5. 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

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


AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05). Data as JSON: /api/errors/cf3336eaef53bc54. Report an issue: GitHub.