{"record":{"id":"e76e9b323f669556","repo":"vxcontrol/pentagi","slug":"failed-to-read-response-body-w","errorCode":null,"errorMessage":"failed to read response body: %w","messagePattern":"failed to read response body: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/searchers/duckduckgo.go","lineNumber":188,"sourceCode":"\t\t}\n\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tresp.Body.Close()\n\t\t\tif attempt == duckduckgoMaxRetries-1 {\n\t\t\t\treturn \"\", fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n\t\t\t}\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn \"\", ctx.Err()\n\t\t\tcase <-time.After(time.Second):\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tbody, err := io.ReadAll(resp.Body)\n\t\tresp.Body.Close()\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to read response body: %w\", err)\n\t\t}\n\n\t\tresponse, err = d.parseHTMLResponse(body)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to parse search response: %w\", err)\n\t\t}\n\n\t\tbreak\n\t}\n\n\tif response == nil || len(response.Results) == 0 {\n\t\treturn \"No results found\", nil\n\t}\n\n\t// Limit results to requested number\n\tif len(response.Results) > maxResults {\n\t\tresponse.Results = response.Results[:maxResults]\n\t}","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/searchers/duckduckgo.go#L170-L206","documentation":"After a 200 response, io.ReadAll(resp.Body) failed while draining the DuckDuckGo HTML body. Typical causes are the connection dropping mid-body or the client timeout firing during the read. The search aborts without retry and Handle converts it into a Fatal engine error.","triggerScenarios":"Network interruption or reset while reading the response stream; duckduckgoTimeout elapsing during body transfer (client.Timeout covers body reads); server closing connection early; context cancellation.","commonSituations":"Slow/unstable links or flaky proxies where the 200 arrives but the body stalls; large anomaly/challenge pages from DuckDuckGo exceeding the timeout; VPN disconnections mid-request.","solutions":["Retry the search — a transient mid-body reset usually succeeds on a second run.","Increase duckduckgoTimeout if body transfer is routinely cut off on slow networks.","Check proxy stability if traffic goes through an intermediary that drops long transfers.","Consider an io.LimitedReader cap so oversized/bogus bodies fail fast instead of hitting timeout."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// ensure generous client timeout so body reads are not cut off\nif client.Timeout < 30*time.Second {\n    client.Timeout = 30 * time.Second\n}","typeGuard":"var netErr net.Error\nif errors.As(err, &netErr) && netErr.Timeout() {\n    // timeout during body read; retry is appropriate\n}","tryCatchPattern":"body, err := io.ReadAll(resp.Body)\nresp.Body.Close()\nif err != nil {\n    if errors.Is(err, context.Canceled) {\n        return \"\", err\n    }\n    return \"\", retryable(fmt.Errorf(\"failed to read response body: %w\", err))\n}","preventionTips":["Set client.Timeout comfortably above expected transfer time","Cap the read with io.LimitReader to fail fast on anomalous bodies","Check proxy stability when body reads intermittently fail","Treat mid-body resets as retryable rather than fatal where possible"],"tags":["network","io","timeout"],"backgroundTag":"body-read-error","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}