gastownhall/beads · error

failed to fetch issues since %s: %w

Error message

failed to fetch issues since %s: %w

What it means

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.

Source

Thrown at internal/gitlab/client.go:314

		case <-ctx.Done():
			return allIssues, ctx.Err()
		default:
		}

		params := map[string]string{
			"per_page":      strconv.Itoa(MaxPageSize),
			"page":          strconv.Itoa(page),
			"updated_after": sinceStr,
		}
		if state != "" && state != "all" {
			params["state"] = state
		}
		applyFilter(params, filter)

		urlStr := c.buildURL(c.issuesBasePath(), params)
		respBody, headers, err := c.doRequest(ctx, http.MethodGet, urlStr, nil)
		if err != nil {
			return nil, fmt.Errorf("failed to fetch issues since %s: %w", sinceStr, err)
		}

		var issues []Issue
		if err := json.Unmarshal(respBody, &issues); err != nil {
			return nil, fmt.Errorf("failed to parse issues response: %w", err)
		}

		allIssues = append(allIssues, issues...)

		// Check for next page
		nextPage := headers.Get("X-Next-Page")
		if nextPage == "" {
			break
		}
		page++

		// Guard against infinite pagination loops from malformed responses
		if page > MaxPages {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Retry the sync after checking network/connectivity; honor Retry-After on 429
  2. Refresh the GitLab token and re-check scopes (read_api)
  3. Verify the since timestamp is valid UTC (RFC3339) and not in the future
  4. Inspect the wrapped %w error for status code and take corresponding action

Example fix

// before
issues, err := client.FetchIssuesSince(ctx, "all", lastSync)
// after
issues, err := client.FetchIssuesSince(ctx, "all", time.Now().UTC().Add(-time.Hour))
if err != nil {
	if errors.Is(err, context.DeadlineExceeded) { /* retry with backoff */ }
	log.Printf("incremental sync failed: %v", err)
}
Defensive patterns

Strategy: retry

Validate before calling

if since.IsZero() || since.After(time.Now().UTC()) {
	return errors.New("since timestamp must be a valid past UTC time")
}
if token == "" { return errors.New("gitlab token is empty") }

Type guard

func isFetchSinceNetworkErr(err error) bool {
	var ne net.Error
	return errors.As(err, &ne) || errors.Is(err, context.DeadlineExceeded)
}

Try / catch

var issues []gitlab.Issue
err := retry.Do(3, 2*time.Second, func() error {
	var e error
	issues, e = client.FetchIssuesSince(ctx, "all", lastSync)
	return e // retry transient network errors; stop on 401/403
})

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/a484ba68dee6f50a. Report an issue: GitHub.