gastownhall/beads · error

failed to fetch issue %d: %w

Error message

failed to fetch issue %d: %w

What it means

FetchIssueByIID performs a GET to /projects/:id/issues/:iid via doRequest, which handles auth, retries, and HTTP error responses. This error wraps any doRequest failure: network errors, timeouts, context cancellation, or non-success HTTP statuses returned by GitLab. It is raised before JSON parsing, so the request itself never completed successfully.

Source

Thrown at internal/gitlab/client.go:401

	respBody, _, err := c.doRequest(ctx, http.MethodGet, urlStr, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get issue links: %w", err)
	}

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

	return links, nil
}

// FetchIssueByIID retrieves a single issue by its project-scoped IID.
func (c *Client) FetchIssueByIID(ctx context.Context, iid int) (*Issue, error) {
	urlStr := c.buildURL("/projects/"+c.projectPath()+"/issues/"+strconv.Itoa(iid), nil)
	respBody, _, err := c.doRequest(ctx, http.MethodGet, urlStr, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to fetch issue %d: %w", iid, err)
	}

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

	return &issue, nil
}

// FetchMilestones retrieves milestones from the project with optional state filter.
// state can be: "active", "closed", or "" (all).
func (c *Client) FetchMilestones(ctx context.Context, state string) ([]Milestone, error) {
	params := map[string]string{
		"per_page": strconv.Itoa(MaxPageSize),
	}
	if state != "" {
		params["state"] = state

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap the error (errors.Unwrap/errors.As on *gitlab.APIError or url.Error) to see whether it is 404, 401/403, or a transport failure
  2. Verify the configured project matches the project that owns the IID and that the IID is an integer within the project
  3. Check token validity and scopes (read_api) and re-authenticate if 401/403
  4. For 404, confirm the issue exists via the GitLab web UI or API search; for network errors, retry with backoff after checking connectivity

Example fix

// before
issue, err := client.FetchIssueByIID(ctx, iid)
if err != nil { return err }
// after
issue, err := client.FetchIssueByIID(ctx, iid)
if err != nil {
    if errors.Is(err, context.Canceled) { return err }
    log.Printf("fetch issue %d failed: %v", iid, err)
    return fmt.Errorf("lookup issue %d: %w", iid, err)
}
Defensive patterns

Strategy: retry

Validate before calling

if iid <= 0 {
    return fmt.Errorf("invalid issue IID %d", iid)
}
if err := ctx.Err(); err != nil {
    return err // context already cancelled
}

Type guard

func isNotFound(err error) bool {
    return strings.Contains(err.Error(), "404")
}

Try / catch

issue, err := client.FetchIssueByIID(ctx, iid)
if err != nil {
    if isNotFound(err) {
        return nil // treat missing issue as empty, not fatal
    }
    if errors.Is(err, context.DeadlineExceeded) || isTransient(err) {
        return fetchWithRetry(ctx, iid)
    }
    return fmt.Errorf("fetch issue %d: %w", iid, err)
}

Prevention

When it happens

Trigger: Calling Client.FetchIssueByIID with an iid that does not exist (GitLab returns 404), an invalid/expired token (401/403), no network connectivity, or a context that was cancelled before/at request time.

Common situations: Typo'd or stale issue IID cached from another project; wrong project configured on the client so the scoped IID resolves to nothing; PAT expired or lacking read_api scope; offline/VPN-down environments.

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/f12bbd195a4c2c16. Report an issue: GitHub.