{"record":{"id":"ca1d5b3f6e265c25","repo":"Tencent/WeKnora","slug":"failed-to-read-response-w-ca1d5b","errorCode":null,"errorMessage":"failed to read response: %w","messagePattern":"failed to read response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/web_search/tavily.go","lineNumber":98,"sourceCode":"\t\treturn nil, fmt.Errorf(\"failed to create request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\tresp, err := p.client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to execute request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\trespBody, _ := io.ReadAll(resp.Body)\n\t\tlogger.Warnf(ctx, \"[WebSearch][Tavily] API returned status %d: %s\", resp.StatusCode, string(respBody))\n\t\treturn nil, fmt.Errorf(\"tavily API returned status %d: %s\", resp.StatusCode, string(respBody))\n\t}\n\n\trespBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read response: %w\", err)\n\t}\n\n\tvar respData tavilySearchResponse\n\tif err := json.Unmarshal(respBody, &respData); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to unmarshal response: %w\", err)\n\t}\n\n\tresults := make([]*types.WebSearchResult, 0, len(respData.Results))\n\tfor _, item := range respData.Results {\n\t\tresult := &types.WebSearchResult{\n\t\t\tTitle:   item.Title,\n\t\t\tURL:     item.URL,\n\t\t\tSnippet: item.Content,\n\t\t\tSource:  \"tavily\",\n\t\t}\n\t\tif includeDate && item.PublishedDate != \"\" {\n\t\t\tif t, err := time.Parse(time.RFC3339, item.PublishedDate); err == nil {\n\t\t\t\tresult.PublishedAt = &t","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/web_search/tavily.go#L80-L116","documentation":"Reading the Tavily response body with io.ReadAll failed after a 200 status. This is rare — typically a connection reset mid-body, truncated transfer, or context cancellation while streaming the response.","triggerScenarios":"Calling provider.Search when the TCP connection drops while reading the response body, the server closes the connection early, or the request context is cancelled mid-read.","commonSituations":"Flaky networks, load balancers/proxies with aggressive idle timeouts, very large response bodies interrupted, container network instability.","solutions":["Retry the request — this is usually transient.","Check proxy/load-balancer timeout configurations between the client and Tavily.","Ensure the context deadline exceeds the expected response time; cancel context causes read errors.","Inspect the wrapped error with errors.Is(err, context.Canceled) vs net errors to distinguish cancellation from network issues."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// nothing to validate pre-call; ensure context has adequate deadline\nctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\ndefer cancel()","typeGuard":null,"tryCatchPattern":"results, err := provider.Search(ctx, q, 5, false)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to read response\") && errors.Is(err, context.Canceled) == false {\n        return retryWithBackoff(ctx, 3) // transient body-read failure\n    }\n    return err\n}","preventionTips":["Use retry with idempotent search requests","Check proxy/LB idle timeouts are longer than API latency","Avoid cancelling contexts mid-response unnecessarily","Monitor connection reset metrics in your network layer"],"tags":["network","io","web-search","tavily"],"backgroundTag":"response-body-read-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}