{"record":{"id":"7c3beb62c981ae0c","repo":"gastownhall/beads","slug":"failed-to-parse-projects-response-w","errorCode":null,"errorMessage":"failed to parse projects response: %w","messagePattern":"failed to parse projects response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/ado/client.go","lineNumber":577,"sourceCode":"\t_, err := c.doRequest(ctx, http.MethodPatch, urlStr, \"application/json-patch+json\", ops)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to remove work item link: %w\", err)\n\t}\n\treturn nil\n}\n\n// ListProjects returns all team projects in the organization.\n// This is an org-level endpoint, not project-scoped.\nfunc (c *Client) ListProjects(ctx context.Context) ([]Project, error) {\n\turlStr := addAPIVersion(c.orgBase() + \"/projects\")\n\trespBody, err := c.doRequest(ctx, http.MethodGet, urlStr, \"\", nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to list projects: %w\", err)\n\t}\n\n\tvar envelope listResponse\n\tif err := json.Unmarshal(respBody, &envelope); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse projects response: %w\", err)\n\t}\n\n\tvar projects []Project\n\tif err := json.Unmarshal(envelope.Value, &projects); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse projects value: %w\", err)\n\t}\n\treturn projects, nil\n}\n\n// GetWorkItemTypes returns the work item types available in the project.\nfunc (c *Client) GetWorkItemTypes(ctx context.Context) ([]WorkItemType, error) {\n\turlStr := addAPIVersion(c.apiBase() + \"/wit/workitemtypes\")\n\trespBody, err := c.doRequest(ctx, http.MethodGet, urlStr, \"\", nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to get work item types: %w\", err)\n\t}\n\n\tvar envelope listResponse","sourceCodeStart":559,"sourceCodeEnd":595,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/ado/client.go#L559-L595","documentation":"This error means ListProjects successfully got an HTTP response, but the body could not be decoded into the library's generic list envelope (json.Unmarshal on the raw body failed). This usually indicates the response is not the expected {\"value\": [...], \"count\": n} JSON — e.g. an HTML error page, HTML login redirect, or truncated body.","triggerScenarios":"Calling ListProjects when the server returns 200/other with a non-JSON body: proxy/login HTML pages, HTML error pages behind a misconfigured URL, empty body, or BOM/encoding issues in the response.","commonSituations":"Corporate proxies returning an HTML auth page with 200; hitting the wrong URL that serves HTML; ADO outage returning an error page; TLS-terminating proxy mangling the response.","solutions":["Log the raw response body to see what was actually returned (HTML page? empty? partial JSON?)","Verify you are not behind an auth proxy that intercepts the request and returns HTML","Confirm the org URL is the ADO API endpoint, not the web UI host","Retry — truncated bodies from transient network issues decode badly","Update the library if ADO changed its API response shape for projects"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"projects, err := client.ListProjects(ctx)\nif err != nil {\n  if strings.Contains(err.Error(), \"failed to parse projects response\") {\n    // non-JSON body: log it for diagnosis, likely proxy/auth HTML\n    return nil, fmt.Errorf(\"non-JSON response from ADO (proxy or auth page?): %w\", err)\n  }\n  return nil, err\n}","preventionTips":["Ensure no auth proxy intercepts dev.azure.com traffic","Run a manual curl of the projects endpoint to confirm JSON responses","Watch ADO status for service degradations","Keep the library updated for ADO response-shape changes"],"tags":["json","parsing","azure-devops","response-shape"],"backgroundTag":"json-decode-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}