{"record":{"id":"6a9e9a13e11d1160","repo":"gastownhall/beads","slug":"max-retries-d-exceeded-w-6a9e9a","errorCode":null,"errorMessage":"max retries (%d) exceeded: %w","messagePattern":"max retries \\((.+?)\\) exceeded: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/client.go","lineNumber":436,"sourceCode":"\t\t\tData   json.RawMessage `json:\"data\"`\n\t\t\tErrors []GraphQLError  `json:\"errors,omitempty\"`\n\t\t}\n\t\tif err := json.Unmarshal(respBody, &gqlResp); err != nil {\n\t\t\treturn nil, lastStatus, fmt.Errorf(\"failed to parse response: %w (body: %s)\", err, string(respBody))\n\t\t}\n\n\t\tif len(gqlResp.Errors) > 0 {\n\t\t\terrMsgs := make([]string, len(gqlResp.Errors))\n\t\t\tfor i, e := range gqlResp.Errors {\n\t\t\t\terrMsgs[i] = e.Message\n\t\t\t}\n\t\t\treturn nil, lastStatus, fmt.Errorf(\"GraphQL errors: %s\", strings.Join(errMsgs, \"; \"))\n\t\t}\n\n\t\treturn gqlResp.Data, lastStatus, nil\n\t}\n\n\treturn nil, lastStatus, fmt.Errorf(\"max retries (%d) exceeded: %w\", MaxRetries+1, lastErr)\n}\n\n// FetchIssues retrieves issues from Linear with optional filtering by state.\n// state can be: \"open\" (unstarted/started), \"closed\" (completed/canceled), or \"all\".\n// If ProjectID is set on the client, only issues from that project are returned.\nfunc (c *Client) FetchIssues(ctx context.Context, state string) ([]Issue, error) {\n\tvar allIssues []Issue\n\tvar cursor string\n\n\tfilter := map[string]interface{}{\n\t\t\"team\": map[string]interface{}{\n\t\t\t\"id\": map[string]interface{}{\n\t\t\t\t\"eq\": c.TeamID,\n\t\t\t},\n\t\t},\n\t}\n\n\t// Add project filter if configured","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/client.go#L418-L454","documentation":"executeOnce retries transient failures (network errors, unreadable bodies, and 429 rate limits) up to MaxRetries+1 attempts; when all attempts fail it wraps the last error with this message. It means the Linear API was never successfully reached or kept returning a retryable failure for the entire retry window.","triggerScenarios":"Client.Execute → executeOnce when every attempt fails: persistent 429 rate-limiting without a recoverable Retry-After, repeated network timeouts/DNS failures, or context cancellation mid-backoff (lastErr will be a 'rate limited (attempt N/M)' or 'request failed (attempt N/M)' wrapper).","commonSituations":"Bursty sync jobs exhausting Linear's rate limit; corporate proxy or VPN blocking api.linear.ly; very short context timeouts that expire during backoff sleeps; Linear status incidents.","solutions":["Inspect the wrapped lastErr after the colon — it names the root cause (rate limit vs network)","If rate limited: reduce call frequency, raise linear.rate_limit_floor awareness, or schedule syncs further apart","If network: verify connectivity to the Linear endpoint and proxy settings","Increase the context deadline so retries aren't cut off by cancellation","Check https://status.linear.app for an ongoing outage before further debugging"],"exampleFix":"// before: one tight loop with a short timeout\nctx, cancel := context.WithTimeout(ctx, 2*time.Second)\n// after: give retries room to work\nctx, cancel := context.WithTimeout(ctx, 60*time.Second)\nissues, err := client.FetchIssues(ctx, \"open\")\nif err != nil && strings.Contains(err.Error(), \"max retries\") {\n    log.Printf(\"Linear unreachable after retries: %v\", err) // inspect wrapped cause\n}","handlingStrategy":"retry","validationCode":"// Before calling, ensure a generous deadline so internal retries can complete\nctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)\ndefer cancel()","typeGuard":null,"tryCatchPattern":"// detect retry exhaustion and re-run with a longer backoff\n_, err := client.Execute(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"max retries (\") {\n    time.Sleep(30 * time.Second) // or requeue the job\n    _, err = client.Execute(ctx, req)\n}","preventionTips":["Use context timeouts well larger than MaxRetries × backoff","Space out bulk syncs to avoid 429 storms","Monitor X-RateLimit-Requests-Remaining headers","Alert on Linear status page incidents","Check network/proxy reachability to the endpoint at startup"],"tags":["retry","rate-limit","network","linear"],"backgroundTag":"max-retries-exceeded","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}