{"record":{"id":"fdaa2f13a93e7746","repo":"gastownhall/beads","slug":"failed-to-read-response-attempt-d-d-w","errorCode":null,"errorMessage":"failed to read response (attempt %d/%d): %w","messagePattern":"failed to read response \\(attempt (.+?)/(.+?)\\): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/gitlab/client.go","lineNumber":142,"sourceCode":"\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to create request: %w\", err)\n\t\t}\n\n\t\treq.Header.Set(\"PRIVATE-TOKEN\", c.Token)\n\t\treq.Header.Set(\"Content-Type\", \"application/json\")\n\n\t\tresp, err := c.HTTPClient.Do(req)\n\t\tif err != nil {\n\t\t\tlastErr = fmt.Errorf(\"request failed (attempt %d/%d): %w\", attempt+1, MaxRetries+1, err)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Limit response body to 50MB to prevent OOM from malformed responses.\n\t\tconst maxResponseSize = 50 * 1024 * 1024\n\t\trespBody, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))\n\t\t_ = resp.Body.Close() // Best effort: HTTP body close; connection may be reused regardless\n\t\tif err != nil {\n\t\t\tlastErr = fmt.Errorf(\"failed to read response (attempt %d/%d): %w\", attempt+1, MaxRetries+1, err)\n\t\t\tcontinue\n\t\t}\n\n\t\tif resp.StatusCode >= 200 && resp.StatusCode < 300 {\n\t\t\treturn respBody, resp.Header, nil\n\t\t}\n\n\t\t// Retry on rate-limiting and server errors with exponential backoff.\n\t\tretriable := resp.StatusCode == http.StatusTooManyRequests ||\n\t\t\tresp.StatusCode == http.StatusInternalServerError ||\n\t\t\tresp.StatusCode == http.StatusBadGateway ||\n\t\t\tresp.StatusCode == http.StatusServiceUnavailable ||\n\t\t\tresp.StatusCode == http.StatusGatewayTimeout\n\n\t\tif retriable {\n\t\t\tdelay := RetryDelay * time.Duration(1<<attempt)\n\t\t\tuseServerDelay := false\n","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/gitlab/client.go#L124-L160","documentation":"Reading the response body with io.ReadAll (bounded to 50MB via io.LimitReader) failed on this attempt. The error is recorded as lastErr with attempt counters and the loop retries; if all attempts fail it surfaces via the 'max retries exceeded' wrapper.","triggerScenarios":"The connection was reset or timed out while streaming the response body (server closed mid-response, network blip, keep-alive race), causing the underlying read to return a transport error.","commonSituations":"Unstable network or flaky LB dropping connections mid-response; GitLab server OOM/restart while responding; aggressive intermediate proxies cutting long responses; very large issue list responses over slow links.","solutions":["Simply retrying often helps - the client already retries; if final failure persists, check network stability","Reduce page size / use the filter parameters so responses are smaller and faster to read","Check for middleboxes (proxy/VPN) resetting long-lived connections","Inspect the wrapped lastErr in 'max retries exceeded' to confirm it's a read/body error"],"exampleFix":"// before\nissues, _, err := client.ListIssues(ctx, filter) // flaky network, body read fails\n// after\nif err != nil { time.Sleep(backoff); issues, _, err = client.ListIssues(ctx, filter) } // caller-level retry with backoff","handlingStrategy":"retry","validationCode":"// no pre-call validation possible; ensure stable network\nif err := checkConnectivity(host); err != nil { return err }","typeGuard":"var netErr net.Error\nif errors.As(err, &netErr) && netErr.Timeout() { /* mid-body timeout */ }","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"failed to read response\") {\n\ttime.Sleep(jitteredBackoff(attempt))\n\tretry()\n}","preventionTips":["Use smaller page sizes/filters so responses complete quickly","Avoid flaky VPN/proxy paths for CI sync jobs","Let the client's internal retry loop handle one-off resets; add outer backoff for persistent ones"],"tags":["network","http-body","retry","gitlab"],"backgroundTag":"response-read-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}