gastownhall/beads · error
request failed (attempt %d/%d): %w
Error message
request failed (attempt %d/%d): %w
What it means
This error wraps a transport-level failure from c.HTTPClient.Do() inside the Linear GraphQL client's retry loop (executeOnce, internal/linear/client.go:378). It means the HTTP request never completed — DNS, TCP, TLS, or context cancellation failed before a response status was available. The client retries up to MaxRetries+1 attempts; if all attempts fail, it is surfaced wrapped as 'max retries exceeded'.
Source
Thrown at internal/linear/client.go:378
if rlErr := c.circuitBreakerError(); rlErr != nil {
return nil, lastStatus, rlErr
}
httpReq, err := http.NewRequestWithContext(ctx, "POST", c.Endpoint, bytes.NewReader(body))
if err != nil {
return nil, 0, fmt.Errorf("failed to create request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
authValue, err := c.authHeader()
if err != nil {
return nil, 0, err
}
httpReq.Header.Set("Authorization", authValue)
resp, err := c.HTTPClient.Do(httpReq)
if err != nil {
lastErr = fmt.Errorf("request failed (attempt %d/%d): %w", attempt+1, MaxRetries+1, err)
continue
}
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
}
lastStatus = resp.StatusCode
rl := parseRateLimitHeaders(resp.Header)
c.recordRateLimitHeaders(rl)
if resp.StatusCode == http.StatusTooManyRequests {
delay := rl.RetryAfter
if delay == 0 {
delay = RetryDelay * time.Duration(1<<attempt) // Exponential backoffView on GitHub (pinned to 71377f2769)
Solutions
- Check basic connectivity: curl -v https://api.linear.to/graphql from the same host
- Inspect the wrapped inner error (errors.Unwrap / %w chain) to distinguish DNS vs timeout vs TLS
- Increase the context deadline or HTTPClient.Timeout if attempts die mid-flight
- Fix proxy env vars (HTTPS_PROXY) or add the Linear endpoint to NO_PROXY
- If offline, fix networking first — the client will keep retrying until MaxRetries is exhausted
Example fix
// before: no timeout, opaque transport failure
client := &linear.Client{Endpoint: "https://api.linear.to/graphql"}
data, err := client.Execute(ctx, req)
// after: explicit timeout + unwrapping the cause
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
data, err := client.Execute(ctx, req)
if err != nil {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
log.Printf("linear request timed out: %v", netErr)
}
} Defensive patterns
Strategy: retry
Validate before calling
func checkLinearReachable(ctx context.Context) error {
c, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(c, http.MethodHead, "https://api.linear.to", nil)
resp, err := http.DefaultClient.Do(req)
if err != nil { return err }
resp.Body.Close()
return nil
} Type guard
func isTransportFailure(err error) bool {
var netErr net.Error
return errors.As(err, &netErr) || strings.Contains(err.Error(), "request failed (attempt")
} Try / catch
data, err := client.Execute(ctx, req)
if err != nil {
if strings.Contains(err.Error(), "request failed (attempt") {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
return fmt.Errorf("linear unreachable (timeout): %w", err)
}
return fmt.Errorf("linear transport failure: %w", err)
}
return err
} Prevention
- Set an explicit HTTPClient.Timeout and context deadline before calling Execute
- Check connectivity/DNS to api.linear.to in startup health checks
- Configure HTTPS_PROXY/NO_PROXY correctly in corporate environments
- Cap caller-side retries so total wait stays bounded
When it happens
Trigger: c.HTTPClient.Do returns a non-nil error: DNS resolution failure for api.linear.to, connection refused/timeout, TLS handshake failure, proxy misconfiguration, or the request context being canceled mid-request. Each failed attempt appends one of these wrapped errors until retries are exhausted.
Common situations: Offline or air-gapped environments, corporate proxies intercepting TLS to api.linear.to, VPN drops, very short context deadlines, invalid LINEAR_API_URL/endpoint configuration, or IPv6 connectivity issues in containers.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- failed to read response (attempt %d/%d): %w
- request failed (attempt %d/%d): %w
- failed to read response (attempt %d/%d): %w
- failed to list projects: %w
- pypi api returned status %d
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/55578089225ae18f.
Report an issue: GitHub.