gastownhall/beads · error

failed to get issue links: %w

Error message

failed to get issue links: %w

What it means

GetIssueLinks failed at the HTTP layer when fetching links (related issues) for an issue IID. The doRequest error is wrapped verbatim.

Source

Thrown at internal/gitlab/client.go:385

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

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

	return &issue, nil
}

// GetIssueLinks retrieves issue links for the specified issue IID.
func (c *Client) GetIssueLinks(ctx context.Context, iid int) ([]IssueLink, error) {
	urlStr := c.buildURL("/projects/"+c.projectPath()+"/issues/"+strconv.Itoa(iid)+"/links", nil)
	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)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped status: 404 → verify iid and projectPath; 401/403 → use a token with read_api and access to the issue
  2. Confirm the GitLab version supports the issue links API (>= 10.3 for basic links)
  3. Fetch the issue first (FetchIssueByIID) to validate iid before listing links
  4. Retry with backoff on 429/5xx

Example fix

// before
links, err := client.GetIssueLinks(ctx, iid)
// after
if _, err := client.FetchIssueByIID(ctx, iid); err != nil {
	return fmt.Errorf("issue %d not found in project: %w", iid, err)
}
links, err := client.GetIssueLinks(ctx, iid)
Defensive patterns

Strategy: validation

Validate before calling

if iid <= 0 { return errors.New("invalid iid") }
resp, _ := http.Get(baseURL + "/api/v4/projects/" + proj + "/issues/" + strconv.Itoa(iid))
if resp.StatusCode == 404 { return fmt.Errorf("issue %d does not exist", iid) }

Type guard

func isLinksFetchErr(err error) bool {
	return strings.Contains(err.Error(), "failed to get issue links")
}

Try / catch

links, err := client.GetIssueLinks(ctx, iid)
if err != nil {
	if strings.Contains(err.Error(), "404") {
		return nil // treat as no links rather than fatal
	}
	return err
}

Prevention

When it happens

Trigger: GET /projects/:id/issues/:iid/links fails: 404 (wrong iid/project path), 401/403 (token lacks read_api or the issue is confidential), 5xx, rate limit, or network/context error.

Common situations: Issue links feature unavailable on older GitLab versions, confidential issues queried with a low-privilege token, wrong project path configured, or IID typo from another project.

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