{"record":{"id":"4a6c1a42e6eac35b","repo":"hashicorp/packer","slug":"http-request-failed-w","errorCode":null,"errorMessage":"HTTP request failed: %w","messagePattern":"HTTP request failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"provisioner/hcp-sbom/packer_release_fetch.go","lineNumber":120,"sourceCode":"func downloadURLToTempFile(ctx context.Context, client *http.Client, url, suffix string) (string, error) {\n\tf, err := os.CreateTemp(\"\", \"packer-dl-*\"+suffix)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create temp file: %w\", err)\n\t}\n\ttmpPath := f.Name()\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)\n\tif err != nil {\n\t\t_ = f.Close()\n\t\t_ = os.Remove(tmpPath)\n\t\treturn \"\", err\n\t}\n\n\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)","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/hashicorp/packer/blob/eb36e3c3e48a036f3e8cc94087636ee72e1303c9/provisioner/hcp-sbom/packer_release_fetch.go#L102-L138","documentation":"After building the GET request for the release artifact, downloadURLToTempFile performs client.Do(req) to download the zip from releases.hashicorp.com. If the HTTP exchange itself fails — before any status code is even available — the temp file is closed and removed and this error is returned with the underlying net/http error wrapped. It indicates a transport-level problem: DNS, TCP, TLS, timeout, or cancelled context, not an HTTP error status (a non-200 yields 'HTTP %d for %s' instead).","triggerScenarios":"client.Do returns an error: DNS resolution failure for releases.hashicorp.com, connection refused/timeout, TLS handshake/certificate errors, the 5-minute client timeout elapses mid-transfer, the context is cancelled, or a proxy is unreachable.","commonSituations":"Build agents without internet access or behind a corporate proxy that requires configuration; DNS misconfiguration; transient network flaps between retries; firewall or egress rules blocking releases.hashicorp.com; slow links exceeding the 5-minute HTTP client timeout while downloading large zips.","solutions":["Read the wrapped cause in the message and verify basic reachability: curl -v https://releases.hashicorp.com/packer/ from the build machine.","Check DNS on the machine (nslookup releases.hashicorp.com) and fix resolver config if it fails.","If behind a corporate proxy, set HTTPS_PROXY/HTTP_PROXY for the process or configure the http.Client Transport's Proxy.","Retry the build — downloadPackerRelease already retries 3 times with 5s delay, so persistent failure means the network path, not transience, is broken.","Check firewall/egress rules to allow HTTPS (443) to releases.hashicorp.com, and confirm the context isn't being cancelled by an outer timeout that is too short."],"exampleFix":"// before: fail hard when the download cannot reach the network\npath, err := downloadURLToTempFile(ctx, client, zipURL, \".zip\")\n// after: surface and handle transient network errors with your own bounded retry\nvar path string\nerr = retry.Config{Tries: 5, RetryDelay: func() time.Duration { return 10 * time.Second }}.\n    Run(ctx, func(ctx context.Context) error {\n        p, err := downloadURLToTempFile(ctx, client, zipURL, \".zip\")\n        if err != nil && isRetryableNetErr(err) { return err }\n        if err != nil { return retry.Fatal(err) }\n        path = p\n        return nil\n    })","handlingStrategy":"retry","validationCode":"// Pre-flight reachability check before starting the download flow\nfunc reachable(endpoint string) error {\n    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n    defer cancel()\n    req, _ := http.NewRequestWithContext(ctx, http.MethodHead, endpoint, nil)\n    resp, err := http.DefaultClient.Do(req)\n    if err != nil {\n        return fmt.Errorf(\"releases host unreachable: %w\", err)\n    }\n    resp.Body.Close()\n    return nil\n}","typeGuard":null,"tryCatchPattern":"var path string\nerr := retry.Config{\n    Tries:      4,\n    RetryDelay: func() time.Duration { return 10 * time.Second },\n}.Run(ctx, func(ctx context.Context) error {\n    p, err := downloadURLToTempFile(ctx, client, url, \".zip\")\n    if err == nil {\n        path = p\n        return nil\n    }\n    if strings.HasPrefix(err.Error(), \"HTTP \") {\n        return retry.Fatal(err) // server responded: status errors are not transient\n    }\n    return err // transport error: safe to retry\n})\nif err != nil {\n    return fmt.Errorf(\"download failed after retries: %w\", err)\n}","preventionTips":["Allow egress to releases.hashicorp.com:443 in firewalls/CI network policies.","Configure HTTPS_PROXY on build agents that sit behind corporate proxies.","Give long downloads a generous deadline; the default client timeout here is 5 minutes.","Run preflight DNS/connectivity checks at CI job start to fail fast with a clear message.","Rely on bounded retries (the caller already retries 3x) instead of single-shot downloads on flaky networks."],"tags":["network","http","dns","timeout","proxy"],"backgroundTag":"http-request-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"}