gastownhall/beads · error
request failed (attempt %d/%d): %w
Error message
request failed (attempt %d/%d): %w
What it means
The HTTP transport itself failed on this attempt (connection refused, DNS failure, TLS error, timeout, context cancellation). The error is stored as lastErr with the attempt counter and the loop continues, retrying up to MaxRetries times. If all attempts fail, the final error is surfaced wrapped in 'max retries exceeded' (error 1778).
Source
Thrown at internal/gitlab/client.go:133
if body != nil {
jsonBody, err := json.Marshal(body)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal request body: %w", err)
}
reqBody = bytes.NewReader(jsonBody)
}
req, err := http.NewRequestWithContext(ctx, method, urlStr, reqBody)
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 ||View on GitHub (pinned to 71377f2769)
Solutions
- Check basic connectivity: curl -v https://your-gitlab/api/v4/version with your token
- Verify DNS/proxy settings (HTTPS_PROXY env) and that the host resolves
- Increase the HTTP client timeout if timeouts are the cause; check context deadlines upstream
- If the final error says 'max retries exceeded', inspect the wrapped lastErr for the root transport cause
Example fix
// before
client := &http.Client{} // default, may time out on slow networks
// after
client := &http.Client{Timeout: 60 * time.Second} Defensive patterns
Strategy: retry
Validate before calling
conn, err := net.DialTimeout("tcp", host+":443", 5*time.Second)
if err != nil { return fmt.Errorf("GitLab host unreachable: %w", err) }
conn.Close() Type guard
var netErr net.Error
if errors.As(err, &netErr) { /* transient network failure */ } Try / catch
var netErr net.Error
if err != nil && errors.As(err, &netErr) {
if netErr.Timeout() { /* back off and retry at app level */ }
} Prevention
- Set a sensible HTTP client timeout for your network conditions
- Verify HTTPS_PROXY/NO_PROXY settings in CI and containers
- Monitor instance health before blaming the client
- Keep the client's built-in retries and add caller-level backoff for long outages
When it happens
Trigger: c.HTTPClient.Do returning a network-level error: unreachable GitLab host, DNS resolution failure, TLS handshake failure, request timeout, or the caller's ctx being canceled mid-request.
Common situations: Corporate proxy blocking gitlab.example.com; VPN down; wrong host/port in config; server temporarily down during maintenance; TLS cert issues with self-hosted GitLab; client-side timeout too low.
Related errors
- failed to read response (attempt %d/%d): %w
- max retries (%d) exceeded: %w
- failed to fetch issue %d: %w
- failed to fetch milestones: %w
- failed to fetch milestone by IID %d: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/3ac686c6fc6f618a.
Report an issue: GitHub.