gastownhall/beads · error
failed to parse issues response
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.
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
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
- 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
- failed to parse issue links response
- failed to parse update response
- failed to create issue
- failed to fetch issue
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/82ce5ea12df77240.
Report an issue: GitHub.
Appendix: 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)