{"record":{"id":"a484ba68dee6f50a","repo":"gastownhall/beads","slug":"failed-to-fetch-issues-since-s-w","errorCode":null,"errorMessage":"failed to fetch issues since %s: %w","messagePattern":"failed to fetch issues since (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/gitlab/client.go","lineNumber":314,"sourceCode":"\t\tcase <-ctx.Done():\n\t\t\treturn allIssues, ctx.Err()\n\t\tdefault:\n\t\t}\n\n\t\tparams := map[string]string{\n\t\t\t\"per_page\":      strconv.Itoa(MaxPageSize),\n\t\t\t\"page\":          strconv.Itoa(page),\n\t\t\t\"updated_after\": sinceStr,\n\t\t}\n\t\tif state != \"\" && state != \"all\" {\n\t\t\tparams[\"state\"] = state\n\t\t}\n\t\tapplyFilter(params, filter)\n\n\t\turlStr := c.buildURL(c.issuesBasePath(), params)\n\t\trespBody, headers, err := c.doRequest(ctx, http.MethodGet, urlStr, nil)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to fetch issues since %s: %w\", sinceStr, err)\n\t\t}\n\n\t\tvar issues []Issue\n\t\tif err := json.Unmarshal(respBody, &issues); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to parse issues response: %w\", err)\n\t\t}\n\n\t\tallIssues = append(allIssues, issues...)\n\n\t\t// Check for next page\n\t\tnextPage := headers.Get(\"X-Next-Page\")\n\t\tif nextPage == \"\" {\n\t\t\tbreak\n\t\t}\n\t\tpage++\n\n\t\t// Guard against infinite pagination loops from malformed responses\n\t\tif page > MaxPages {","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/gitlab/client.go#L296-L332","documentation":"FetchIssuesSince failed on the HTTP request while fetching issues updated after the given timestamp. The library wraps the transport/request error from doRequest, including the RFC3339 since value for context.","triggerScenarios":"GET /projects/:id/issues?updated_after=<ts> fails due to network error, 401/403 auth failure, invalid updated_after format, 5xx from GitLab, or context cancellation mid-request.","commonSituations":"Expired or revoked personal access token, server downtime during incremental sync, offline development, clock-skewed since values, or rate limiting (429) from heavy sync loops.","solutions":["Retry the sync after checking network/connectivity; honor Retry-After on 429","Refresh the GitLab token and re-check scopes (read_api)","Verify the since timestamp is valid UTC (RFC3339) and not in the future","Inspect the wrapped %w error for status code and take corresponding action"],"exampleFix":"// before\nissues, err := client.FetchIssuesSince(ctx, \"all\", lastSync)\n// after\nissues, err := client.FetchIssuesSince(ctx, \"all\", time.Now().UTC().Add(-time.Hour))\nif err != nil {\n\tif errors.Is(err, context.DeadlineExceeded) { /* retry with backoff */ }\n\tlog.Printf(\"incremental sync failed: %v\", err)\n}","handlingStrategy":"retry","validationCode":"if since.IsZero() || since.After(time.Now().UTC()) {\n\treturn errors.New(\"since timestamp must be a valid past UTC time\")\n}\nif token == \"\" { return errors.New(\"gitlab token is empty\") }","typeGuard":"func isFetchSinceNetworkErr(err error) bool {\n\tvar ne net.Error\n\treturn errors.As(err, &ne) || errors.Is(err, context.DeadlineExceeded)\n}","tryCatchPattern":"var issues []gitlab.Issue\nerr := retry.Do(3, 2*time.Second, func() error {\n\tvar e error\n\tissues, e = client.FetchIssuesSince(ctx, \"all\", lastSync)\n\treturn e // retry transient network errors; stop on 401/403\n})","preventionTips":["Refresh tokens before expiry and store lastSync in UTC RFC3339","Add timeouts and retries with backoff around sync calls","Monitor GitLab status/rate limits during sync windows","Keep the sync interval short so each run fetches few pages"],"tags":["gitlab","network","sync"],"backgroundTag":"http-request-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}