hashicorp/packer · error

failed to fetch %s: %w

Error message

failed to fetch %s: %w

What it means

fetchStream performs the raw HTTP GET for remote registry streams (index.json, releases list, etc.). Any transport-level failure — DNS failure, connection refused, TLS error, timeout, context cancellation — is wrapped as "failed to fetch %s: %w" with the underlying cause preserved.

Source

Thrown at packer/plugin-getter/remote/getter.go:126

		out = append(out, plugingetter.Release{Version: "v" + v})
	}
	buf := &bytes.Buffer{}
	if err := json.NewEncoder(buf).Encode(out); err != nil {
		return nil, err
	}
	return io.NopCloser(buf), nil
}

// fetchStream returns the response body of url for the caller to consume
// and close; fetch buffers it, for the small metadata files.
func (g *Getter) fetchStream(url string) (io.ReadCloser, error) {
	if g.HttpClient == nil {
		g.HttpClient = &http.Client{}
	}
	log.Printf("[DEBUG] remote-getter: getting %q", url)
	resp, err := g.HttpClient.Get(url)
	if err != nil {
		return nil, fmt.Errorf("failed to fetch %s: %w", url, err)
	}
	if resp.StatusCode >= 400 {
		_ = resp.Body.Close()
		return nil, fmt.Errorf("%s returned status %d", url, resp.StatusCode)
	}
	return resp.Body, nil
}

func (g *Getter) fetch(url string) ([]byte, error) {
	body, err := g.fetchStream(url)
	if err != nil {
		return nil, err
	}
	defer func() { _ = body.Close() }()
	return io.ReadAll(body)
}

// pluginPath returns the URL path of the plugin below the host, e.g.

View on GitHub (pinned to eb36e3c3e4)

Solutions

  1. Check network connectivity and DNS resolution for the URL in the error message (curl it).
  2. Configure HTTPS_PROXY/HTTP_PROXY if egress requires a proxy.
  3. Retry if the failure was transient (timeouts, resets).
  4. Fix TLS trust (install the corporate CA) if the error is a certificate error.

Example fix

// before
export PACKER_PLUGIN_PATH=...  # behind proxy, fails
// after
export HTTPS_PROXY=http://proxy.corp:3128
packer plugins install ...
Defensive patterns

Strategy: retry

Validate before calling

u, err := url.Parse(registryURL)
if err != nil || u.Host == "" {
    return fmt.Errorf("invalid registry host")
}
if _, err := net.LookupHost(u.Hostname()); err != nil {
    return fmt.Errorf("cannot resolve %s; check network/DNS/proxy", u.Hostname())
}

Try / catch

var body io.ReadCloser
var err error
for i := 0; i < 3; i++ {
    body, err = g.Get("releases", opts)
    if err == nil || !isTransientNetErr(err) {
        break
    }
    time.Sleep(time.Duration(1<<i) * time.Second)
}

Prevention

When it happens

Trigger: g.HttpClient.Get(url) returns a non-nil error during Get/fetch — offline machine, unresolvable hostname, blocked egress, TLS interception, or a canceled context.

Common situations: No internet access or DNS failure when installing plugins; corporate firewall/proxy blocking releases.hashicorp.com or a custom registry host; expired/misconfigured TLS on a self-hosted registry; transient network flaps.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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