gastownhall/beads · error
failed to read response (attempt %d/%d): %w
Error message
failed to read response (attempt %d/%d): %w
What it means
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.
Source
Thrown at internal/gitlab/client.go:142
if err != nil {
return nil, nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("PRIVATE-TOKEN", c.Token)
req.Header.Set("Content-Type", "application/json")
resp, err := c.HTTPClient.Do(req)
if err != nil {
lastErr = fmt.Errorf("request failed (attempt %d/%d): %w", attempt+1, MaxRetries+1, err)
continue
}
// Limit response body to 50MB to prevent OOM from malformed responses.
const maxResponseSize = 50 * 1024 * 1024
respBody, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
_ = resp.Body.Close() // Best effort: HTTP body close; connection may be reused regardless
if err != nil {
lastErr = fmt.Errorf("failed to read response (attempt %d/%d): %w", attempt+1, MaxRetries+1, err)
continue
}
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return respBody, resp.Header, nil
}
// Retry on rate-limiting and server errors with exponential backoff.
retriable := resp.StatusCode == http.StatusTooManyRequests ||
resp.StatusCode == http.StatusInternalServerError ||
resp.StatusCode == http.StatusBadGateway ||
resp.StatusCode == http.StatusServiceUnavailable ||
resp.StatusCode == http.StatusGatewayTimeout
if retriable {
delay := RetryDelay * time.Duration(1<<attempt)
useServerDelay := false
View on GitHub (pinned to 71377f2769)
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
Example fix
// before
issues, _, err := client.ListIssues(ctx, filter) // flaky network, body read fails
// after
if err != nil { time.Sleep(backoff); issues, _, err = client.ListIssues(ctx, filter) } // caller-level retry with backoff Defensive patterns
Strategy: retry
Validate before calling
// no pre-call validation possible; ensure stable network
if err := checkConnectivity(host); err != nil { return err } Type guard
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() { /* mid-body timeout */ } Try / catch
if err != nil && strings.Contains(err.Error(), "failed to read response") {
time.Sleep(jitteredBackoff(attempt))
retry()
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- request failed (attempt %d/%d): %w
- max retries (%d) exceeded: %w
- failed after %d retries: %w
- transient error %d (attempt %d/%d)
- failed to parse issues response: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/fdaa2f13a93e7746.
Report an issue: GitHub.