gastownhall/beads · error

failed to fetch issues: %w

Error message

failed to fetch issues: %w

What it means

FetchIssues calls doRequest to GET the issues endpoint; any failure inside doRequest (auth error, network failure, retries exhausted, malformed request) is wrapped as 'failed to fetch issues'. This is the outer context wrapper - the real cause is in the wrapped chain (e.g. errors 1773-1778).

Source

Thrown at internal/gitlab/client.go:254

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

		params := map[string]string{
			"per_page": strconv.Itoa(MaxPageSize),
			"page":     strconv.Itoa(page),
		}
		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: %w", 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. Print the full error chain (err with %v or errors.Unwrap) - the wrapped cause names the real problem (status code, network error)
  2. Validate the token: curl -H "PRIVATE-TOKEN: $TOKEN" https://gitlab.example/api/v4/user
  3. Verify the configured project path/group resolves in the GitLab UI and is URL-encoded for subgroups
  4. Check connectivity to the instance and whether a 429/5xx outage explains retry exhaustion

Example fix

// before
issues, err := client.FetchIssues(ctx, filter)
log.Println(err) // failed to fetch issues: API error: 401 ...
// after
issues, err := client.FetchIssues(ctx, filter)
if err != nil {
	var apiErr *gitlab.APIError // or check strings for status
	log.Printf("fetch failed: %v", err) // log full chain
}
Defensive patterns

Strategy: try-catch

Validate before calling

if !validBaseURL(cfg.BaseURL) || cfg.Token == "" {
	return fmt.Errorf("GitLab client misconfigured before FetchIssues")
}

Type guard

func fetchFailed(err error) bool { return strings.Contains(err.Error(), "failed to fetch issues") }

Try / catch

issues, err := client.FetchIssues(ctx, filter)
if err != nil {
	switch {
	case strings.Contains(err.Error(), "status 401"): refreshToken()
	case strings.Contains(err.Error(), "transient error"), strings.Contains(err.Error(), "max retries"): scheduleRetry()
	default: return err
	}
}

Prevention

When it happens

Trigger: Any failure of the underlying GET to c.issuesBasePath(): invalid token (401), bad project path (404), rate limit exhausted across retries (429), network outage, or malformed base URL making request creation fail.

Common situations: CI jobs with stale tokens; misconfigured GitLab project/group path; syncing during a GitLab outage; hitting secondary rate limits with large pagination loops; base URL missing scheme.

Related errors


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