gastownhall/beads · error
failed to parse issues response: %w
Error message
failed to parse issues response: %w
What it means
FetchIssues failed to JSON-decode a GitLab issues list response into []Issue. The library throws this when the HTTP response body is not the expected JSON array of issue objects, so the pagination loop aborts with the underlying json.Unmarshal error wrapped.
Source
Thrown at internal/gitlab/client.go:259
params := map[string]string{
"per_page": strconv.Itoa(MaxPageSize),
"page": strconv.Itoa(page),
}
if state != "" && state != "all" {
params["state"] = state
}
applyFilter(params, filter)
urlStr := c.buildURL(c.issuesBasePath(), params)
respBody, headers, err := c.doRequest(ctx, http.MethodGet, urlStr, nil)
if err != nil {
return nil, fmt.Errorf("failed to fetch issues: %w", err)
}
var issues []Issue
if err := json.Unmarshal(respBody, &issues); err != nil {
return nil, fmt.Errorf("failed to parse issues response: %w", err)
}
allIssues = append(allIssues, issues...)
// Check for next page
nextPage := headers.Get("X-Next-Page")
if nextPage == "" {
break
}
page++
// Guard against infinite pagination loops from malformed responses
if page > MaxPages {
return nil, fmt.Errorf("pagination limit exceeded: stopped after %d pages", MaxPages)
}
}
return filterByProject(allIssues, filter), nilView on GitHub (pinned to 71377f2769)
Solutions
- Check baseURL/token config: curl the same URL and confirm the body is a JSON array of issues
- Inspect the wrapped %w error for the exact Unmarshal mismatch (field/type) and compare against the Issue struct for your GitLab version
- Upgrade/downgrade the library to match your GitLab server version
- If behind a proxy, bypass it or fix it so API responses pass through untouched
Example fix
// before
issues, err := client.FetchIssues(ctx, "opened")
if err != nil { panic(err) }
// after
issues, err := client.FetchIssues(ctx, "opened")
if err != nil {
log.Fatalf("gitlab fetch failed (check baseURL/token/GitLab version): %v", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
resp, _ := http.Get(baseURL + "/api/v4/projects/" + url.PathEscape(project) + "/issues?per_page=1")
body, _ := io.ReadAll(resp.Body)
if !json.Valid(body) || body[0] != '[' {
return fmt.Errorf("endpoint does not return a JSON array of issues")
} Type guard
func isJSONParseError(err error) bool {
var jsonErr *json.UnmarshalTypeError
return errors.As(err, &jsonErr) || strings.Contains(err.Error(), "failed to parse issues response")
} Try / catch
issues, err := client.FetchIssues(ctx, "opened")
if err != nil {
var synErr *net.OpError
if errors.Is(err, context.Canceled) { return err }
log.Printf("gitlab response unparseable (check baseURL/proxy/GitLab version): %v", err)
return nil // or fall back to cached issues
} Prevention
- Point the client at the direct GitLab API URL, not an SSO/proxy front end
- Use a token with read_api scope and test it with a one-issue curl first
- Keep the library version aligned with your GitLab server version
- Log raw response bodies on failure to diagnose schema drift quickly
When it happens
Trigger: GET /projects/:id/issues returns a body that does not unmarshal into []Issue — e.g. a proxy/login page returning HTML, a JSON error object instead of an array, or a GitLab schema change adding a field with an incompatible type.
Common situations: Wrong base URL pointing at a non-GitLab host (HTML 200 response), an auth proxy or SSO redirect page, GitLab version with changed field types (e.g. dates or IDs changing shape), or a reverse proxy returning an error page with 200.
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 create response: %w
- failed to parse update response: %w
- failed to parse issue links response: %w
- failed to marshal request body: %w
- request failed (attempt %d/%d): %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/82ce5ea12df77240.
Report an issue: GitHub.