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"] = stateView on GitHub (pinned to 71377f2769)
Solutions
- 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
- Verify the configured project matches the project that owns the IID and that the IID is an integer within the project
- Check token validity and scopes (read_api) and re-authenticate if 401/403
- 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
- Validate IIDs are positive integers and belong to the configured project
- Keep tokens fresh and scoped (read_api); rotate before expiry
- Set a sane HTTP timeout and rely on the client's built-in retry for transient failures
- Cache known-good IIDs and validate against search when a fetch 404s
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
- request failed (attempt %d/%d): %w
- failed to fetch milestones: %w
- failed to fetch milestone by IID %d: %w
- failed to list projects: %w
- pypi api returned status %d
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/f12bbd195a4c2c16.
Report an issue: GitHub.