projectdiscovery/subfinder · warning

failed to close response body

Error message

failed to close response body: %w

What it means

PugRecon closes the HTTP response body in a deferred func; if resp.Body.Close() returns an error, an Error result with this message is emitted. This is a defensive check — close errors on response bodies rarely indicate lost data, since the body was already fully consumed or discarded.

Solutions

  1. Check the wrapped %w cause — if it's a connection-reset, treat the response as unreliable and retry
  2. Verify the status code handling already captured the real failure earlier in the flow
  3. If noise-only, run the source again; the error is typically transient
  4. Avoid custom RoundTrippers that surface spurious close errors

Example fix

// before
if err := resp.Body.Close(); err != nil {
	results <- subscraping.Result{... Error: fmt.Errorf("failed to close response body: %w", err)}
}
// after
if err := resp.Body.Close(); err != nil && resp.StatusCode == http.StatusOK {
	results <- subscraping.Result{... Error: fmt.Errorf("failed to close response body: %w", err)}
}
Defensive patterns

Strategy: try-catch

Try / catch

defer func() {
	if cerr := resp.Body.Close(); cerr != nil {
		log.Printf("non-fatal close error: %v", cerr) // don't fail the run for close errors
	}
}()

Prevention

When it happens

Trigger: The deferred resp.Body.Close() call returns non-nil after the PugRecon API POST completes — e.g. some transports/proxies report errors on close after connection resets.

Common situations: Connection torn down abruptly mid/close by proxy or server; custom HTTP transports returning close errors; usually benign noise alongside a real network issue.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of projectdiscovery/subfinder@7a0b91f0fa (2026-09-06). Data as JSON: /api/errors/73ba90d593c55928. Report an issue: GitHub.

Appendix: source

Thrown at pkg/subscraping/sources/pugrecon/pugrecon.go:86

		// Prepare headers
		headers := map[string]string{
			"Authorization": "Bearer " + randomApiKey,
			"Content-Type":  "application/json",
			"Accept":        "application/json",
		}

		apiURL := "https://pugrecon.com/api/v1/domains"
		s.requests++
		resp, err := session.HTTPRequest(ctx, http.MethodPost, apiURL, "", headers, bodyReader, subscraping.BasicAuth{}) // Use HTTPRequest for full header control
		if err != nil {
			results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
			s.errors++
			session.DiscardHTTPResponse(resp)
			return
		}
		defer func() {
			if err := resp.Body.Close(); err != nil {
				results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: fmt.Errorf("failed to close response body: %w", err)}
				s.errors++
			}
		}()

		if resp.StatusCode != http.StatusOK {
			errorMsg := fmt.Sprintf("received status code %d", resp.StatusCode)
			// Attempt to read error message from body if possible
			var apiResp pugreconAPIResponse
			if json.NewDecoder(resp.Body).Decode(&apiResp) == nil && apiResp.Message != "" {
				errorMsg = fmt.Sprintf("%s: %s", errorMsg, apiResp.Message)
			}
			results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: fmt.Errorf("%s", errorMsg)}
			s.errors++
			return
		}

		var response pugreconAPIResponse
		err = json.NewDecoder(resp.Body).Decode(&response)

View on GitHub (pinned to 7a0b91f0fa)