Report #104616
[bug\_fix] goroutine leak due to not closing HTTP response body
Always defer resp.Body.Close\(\) after a successful http.Get or http.Do call, even if you do not read the body. For example: resp, err := http.Get\(url\); if err \!= nil \{ ... \}; defer resp.Body.Close\(\). This ensures the underlying TCP connection is reused or closed properly.
Journey Context:
I was building a service that periodically fetched metrics from an external API. After running for a few hours, the service started consuming all available file descriptors and became unresponsive. I used pprof to see goroutine counts: thousands of goroutines were stuck in 'io.ReadAll' or 'net/http.\(\*persistConn\).readLoop'. The code was calling http.Get but not closing the response body when the response status was non-200 \(e.g., 404\). The root cause: Go's HTTP client reuses connections; if the body is not closed, the underlying TCP connection remains open and the goroutine reading it never returns. The fix: add 'defer resp.Body.Close\(\)' immediately after checking the error. Now the connections are properly recycled. This is a well-known pitfall documented in the Go blog.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-09-13T20:05:57.900095+00:00— report_created — created