{"record":{"id":"d840b2b057a33fc6","repo":"hashicorp/packer","slug":"failed-reading-response-body-for-s-w","errorCode":null,"errorMessage":"failed reading response body for %s: %w","messagePattern":"failed reading response body for (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"provisioner/hcp-sbom/packer_release_fetch.go","lineNumber":163,"sourceCode":"func 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 {\n\t\treturn \"\", fmt.Errorf(\"failed to download %s: %w\", url, err)\n\t}\n\tdefer func() { _ = resp.Body.Close() }()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"download failed: HTTP %d for %s\", resp.StatusCode, url)\n\t}\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed reading response body for %s: %w\", url, err)\n\t}\n\tif len(strings.TrimSpace(string(body))) == 0 {\n\t\treturn \"\", fmt.Errorf(\"empty response body for %s\", url)\n\t}\n\n\treturn string(body), nil\n}\n\nfunc isValidSHA256Hex(s string) bool {\n\tif len(s) != 64 {\n\t\treturn false\n\t}\n\t_, err := hex.DecodeString(s)\n\treturn err == nil\n}\n\nfunc expectedZipSHA256FromSums(sumsContent, fileName string) (string, error) {\n\tfor _, line := range strings.Split(sumsContent, \"\\n\") {","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/hashicorp/packer/blob/eb36e3c3e48a036f3e8cc94087636ee72e1303c9/provisioner/hcp-sbom/packer_release_fetch.go#L145-L181","documentation":"After a 200 response for the SHA256SUMS file, io.ReadAll failed while draining resp.Body — the connection dropped or a read/timeout error occurred mid-transfer. The checksum content could not be fully retrieved, so downloadChecksumFile aborts and wraps the underlying read error (typically an io.ReadError, unexpected EOF, or context deadline).","triggerScenarios":"io.ReadAll(resp.Body) returns err after a 200 response for <base>/packer/<v>/packer_<v>_SHA256SUMS — server closed the connection mid-body, TLS record corruption, an idle-timeout reset from a proxy/LB, or the request context expired during the body read.","commonSituations":"Flaky network or VPN dropping long-lived connections; an aggressive proxy/load balancer idle timeout; context deadline expiring on a slow link; TLS interception appliances resetting streams; transient releases.hashicorp.com connection resets.","solutions":["Simply retry — downloadPackerRelease already retries the full flow 3 times with a 5s delay, and this error is usually transient","Increase the http.Client timeout (currently 5 minutes in downloadPackerRelease) or the context deadline if on very slow links","Check for middleboxes (proxy, TLS-inspection firewall, VPN) resetting connections to releases.hashicorp.com and bypass them","Check server-side logs or HashiCorp status if resets correlate with service incidents"],"exampleFix":"// before: client built inline with fixed timeout\nclient := &http.Client{Timeout: 5 * time.Minute}\n// after: larger timeout for constrained networks\nclient := &http.Client{Timeout: 15 * time.Minute}","handlingStrategy":"retry","validationCode":"// preflight: confirm the checksums endpoint responds and completes a full small read\nresp, err := client.Get(shaSumsURL)\nif err != nil {\n    return fmt.Errorf(\"checksums endpoint unreachable: %w\", err)\n}\nif resp.StatusCode != http.StatusOK {\n    _ = resp.Body.Close()\n    return fmt.Errorf(\"unexpected status %d probing %s\", resp.StatusCode, shaSumsURL)\n}\nn, err := io.Copy(io.Discard, resp.Body)\n_ = resp.Body.Close()\nif err != nil {\n    return fmt.Errorf(\"connection to %s unstable (read %d bytes then failed): %w\", shaSumsURL, n, err)\n}","typeGuard":"// identify read/timeout errors from the body as transient network problems\nfunc isBodyReadError(err error) bool {\n    var ne net.Error\n    if errors.As(err, &ne) && ne.Timeout() {\n        return true\n    }\n    return errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) || errors.Is(err, syscall.ECONNRESET)\n}","tryCatchPattern":"sums, err := downloadChecksumFile(ctx, client, shaSumsURL)\nif err != nil {\n    if isBodyReadError(errors.Unwrap(errors.Unwrap(err))) || isBodyReadError(err) {\n        return retryErr{err} // transient mid-body disconnect; retry\n    }\n    return err\n}","preventionTips":["Keep http.Client and context timeouts generous on slow links so body reads are not cut off","Bypass or tune TLS-inspection proxies/LBs with aggressive idle timeouts","Prefer wired/stable network or retry automatically for CI runners on VPNs","Treat any mid-body disconnect as transient and lean on the 3-try retry with 5s backoff","Monitor for correlated ECONNRESET patterns pointing at a middlebox rather than random flakiness"],"tags":["go","http","network","io","connection-reset"],"backgroundTag":"response-body-read-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"}