gastownhall/beads · error
failed to list projects: %w
Error message
failed to list projects: %w
What it means
This error wraps an HTTP failure from the org-level GET {_org}/_apis/projects endpoint used by ListProjects. It fires before any JSON decoding, meaning the request itself failed (transport error or non-success status). The original error is preserved via %w.
Source
Thrown at internal/ado/client.go:572
Op: "remove",
Path: fmt.Sprintf("/relations/%d", relationIndex),
},
}
urlStr := addAPIVersion(fmt.Sprintf("%s/wit/workitems/%d", c.apiBase(), sourceID))
_, err := c.doRequest(ctx, http.MethodPatch, urlStr, "application/json-patch+json", ops)
if err != nil {
return fmt.Errorf("failed to remove work item link: %w", err)
}
return nil
}
// ListProjects returns all team projects in the organization.
// This is an org-level endpoint, not project-scoped.
func (c *Client) ListProjects(ctx context.Context) ([]Project, error) {
urlStr := addAPIVersion(c.orgBase() + "/projects")
respBody, err := c.doRequest(ctx, http.MethodGet, urlStr, "", nil)
if err != nil {
return nil, fmt.Errorf("failed to list projects: %w", err)
}
var envelope listResponse
if err := json.Unmarshal(respBody, &envelope); err != nil {
return nil, fmt.Errorf("failed to parse projects response: %w", err)
}
var projects []Project
if err := json.Unmarshal(envelope.Value, &projects); err != nil {
return nil, fmt.Errorf("failed to parse projects value: %w", err)
}
return projects, nil
}
// GetWorkItemTypes returns the work item types available in the project.
func (c *Client) GetWorkItemTypes(ctx context.Context) ([]WorkItemType, error) {
urlStr := addAPIVersion(c.apiBase() + "/wit/workitemtypes")
respBody, err := c.doRequest(ctx, http.MethodGet, urlStr, "", nil)View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped error's status code; 401 means fix the PAT, 404 usually means wrong org URL
- Verify the client's organization base URL (orgBase) is correct and reachable
- Regenerate the PAT with appropriate scope (project/team read access)
- Test the endpoint manually: GET https://dev.azure.com/{org}/_apis/projects?api-version=... with the same PAT
- Rule out network/proxy issues by curling the org URL from the same host
Example fix
// before
cfg := ado.Config{OrgURL: "dev.azure.com/myorg"} // missing scheme
// after
cfg := ado.Config{OrgURL: "https://dev.azure.com/myorg"}
projects, err := client.ListProjects(ctx) Defensive patterns
Strategy: try-catch
Validate before calling
// verify org URL and PAT before calling
if !strings.HasPrefix(orgURL, "https://dev.azure.com/") && !strings.Contains(orgURL, ".visualstudio.com") {
return fmt.Errorf("suspicious org URL: %s", orgURL)
}
resp, err := http.Get(orgURL + "/_apis/projects?api-version=7.1") // smoke test w/ auth header Try / catch
projects, err := client.ListProjects(ctx)
if err != nil {
if strings.Contains(err.Error(), "401") || strings.Contains(err.Error(), "TF") {
return nil, fmt.Errorf("auth failed listing projects — check PAT scope/expiry: %w", err)
}
return nil, err
} Prevention
- Use the full https:// scheme in the organization URL
- Keep PATs scoped and check expiry in long-running services
- Smoke-test connectivity to dev.azure.com from the deployment environment
- Cache project lists to reduce exposure to transient failures
When it happens
Trigger: Calling Client.ListProjects(ctx) when doRequest fails: unauthenticated/invalid PAT (401), wrong organization URL in client config (404/DNS failure), network outage, or ADO returning 203/throttling responses treated as failures.
Common situations: Misconfigured organization URL (e.g. missing https:// or wrong org name); PAT without scope for the org; offline/VPN-restricted environments; org renamed or deleted.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- failed to get work item types: %w
- ado.pat not configured: set via 'bd config set ado.pat <toke
- failed to list projects: %w
- transient error %d (attempt %d/%d)
- max retries (%d) exceeded: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c3413493e6252d52.
Report an issue: GitHub.