gastownhall/beads · error

failed to parse issue links response: %w

Error message

failed to parse issue links response: %w

What it means

GetIssueLinks in the GitLab client calls the GitLab REST endpoint /projects/:id/issues/:iid/links and unmarshals the response body into []IssueLink. This error wraps the json.Unmarshal failure when the HTTP response body is not a valid JSON array of issue links. It only fires after doRequest succeeded (HTTP completed and was not treated as an API error), so the body arrived but was malformed or an unexpected shape.

Source

Thrown at internal/gitlab/client.go:390

	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)
	}

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

View on GitHub (pinned to 71377f2769)

Solutions

  1. Log the raw response body and the wrapped json.Unmarshal error (e.g. errors.Unwrap) to see whether the body is HTML, empty, or differently shaped
  2. Verify the request is reaching the real GitLab API host and not an SSO/proxy redirect; check GITLAB_HOST/token configuration
  3. Re-run with a curl of the same endpoint to confirm the API returns a JSON array
  4. Retry after transient gateway errors; if a GitLab upgrade changed the schema, update the IssueLink struct to match the actual payload

Example fix

// before
links, err := client.GetIssueLinks(ctx, 42)
if err != nil {
    return err
}
// after
links, err := client.GetIssueLinks(ctx, 42)
if err != nil {
    var jsonErr *json.UnmarshalTypeError
    if errors.As(err, &jsonErr) {
        log.Printf("non-JSON or unexpected payload from links endpoint: %v", jsonErr)
    }
    return fmt.Errorf("GetIssueLinks: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the GitLab host/token are set before any API call
if client == nil || strings.TrimSpace(gitlabHost) == "" || strings.TrimSpace(token) == "" {
    return errors.New("gitlab host/token must be configured")
}

Type guard

func isParseFailure(err error) bool {
    var se *json.SyntaxError
    var te *json.UnmarshalTypeError
    return errors.As(err, &se) || errors.As(err, &te)
}

Try / catch

links, err := client.GetIssueLinks(ctx, iid)
if err != nil {
    if isParseFailure(err) {
        // response was non-JSON (proxy/SSO page) — do not retry blindly
        return fmt.Errorf("gitlab returned non-JSON links payload: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Client.GetIssueLinks when the GitLab server (or a proxy/gateway in front of it) returns HTML (login page, error page), an empty/truncated body, or JSON whose top level is an object instead of an array of IssueLink.

Common situations: Corporate proxies or SSO redirects injecting HTML into API responses; reverse proxies returning 502/504 error pages as HTML with a 200-like path; GitLab version changes altering the links payload; hitting a rate-limit page instead of the JSON API.

Understand the failure class

Related errors


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