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
- 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
- Verify the request is reaching the real GitLab API host and not an SSO/proxy redirect; check GITLAB_HOST/token configuration
- Re-run with a curl of the same endpoint to confirm the API returns a JSON array
- 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
- Point the client base URL at the API host, never the web UI or an SSO-protected page
- Log response bodies on parse failures to distinguish HTML proxies from schema drift
- Keep the IssueLink struct aligned with your GitLab version's API
- Check for proxy/VPN interference when errors cluster in one network environment
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to parse issues response: %w
- failed to parse create response: %w
- failed to parse update response: %w
- failed to parse issue response: %w
- failed to parse milestones response: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/d717a7c96c1b69b6.
Report an issue: GitHub.