gastownhall/beads · error
failed to list projects: %w
Error message
failed to list projects: %w
What it means
Wraps a failure from client.ListProjects(ctx) in runADOProjects, i.e. the HTTP call to Azure DevOps to enumerate projects failed after the client was built successfully. The cause (network error, 401/403, 404 wrong org) is preserved via %w. This is a runtime/API-layer error, not a configuration error.
Source
Thrown at cmd/bd/ado.go:442
cfg := getADOConfig()
if cfg.PAT == "" {
return fmt.Errorf("ado.pat not configured: set via 'bd config set ado.pat <token>' or AZURE_DEVOPS_PAT env var")
}
if cfg.Org == "" && cfg.URL == "" {
return fmt.Errorf("ado.org not configured: set via 'bd config set ado.org <org>' or AZURE_DEVOPS_ORG env var")
}
out := cmd.OutOrStdout()
client, err := getADOClient(cfg)
if err != nil {
return fmt.Errorf("invalid ADO configuration: %w", err)
}
ctx := context.Background()
projects, err := client.ListProjects(ctx)
if err != nil {
return fmt.Errorf("failed to list projects: %w", err)
}
if jsonOutput {
return outputJSON(projects)
}
_, _ = fmt.Fprintln(out, "Azure DevOps Projects")
_, _ = fmt.Fprintln(out, "=====================")
for _, p := range projects {
_, _ = fmt.Fprintf(out, " %s\n", p.Name)
if p.Description != "" {
_, _ = fmt.Fprintf(out, " %s\n", p.Description)
}
}
if len(projects) == 0 {
_, _ = fmt.Fprintln(out, "No projects found")
}View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped message for the HTTP status or transport error.
- If 401/203: regenerate the PAT in Azure DevOps (must include Project/team read scope) and `bd config set ado.pat <token>` or update AZURE_DEVOPS_PAT.
- If 404: verify the org name via `bd config get ado.org`.
- If network/proxy: check connectivity to https://dev.azure.com and proxy env vars (HTTPS_PROXY).
- Retry after fixing; transient 5xx can be retried as-is.
Example fix
// before Error: failed to list projects: ADO API error: TF400813 Resource unavailable: unauthorized // after $ bd config set ado.pat <new-pat-with-read-scope> $ bd ado projects
Defensive patterns
Strategy: try-catch
Validate before calling
if os.Getenv("AZURE_DEVOPS_PAT") == "" && cfg.PAT == "" {
return errors.New("PAT not configured; skipping ADO call")
}
// optional pre-flight:
resp, err := http.Head("https://dev.azure.com/" + org)
if err != nil || resp.StatusCode >= 500 { return fmt.Errorf("ADO unreachable") } Type guard
func isAuthError(err error) bool {
return err != nil && (strings.Contains(err.Error(), "401") || strings.Contains(err.Error(), "unauthorized"))
} Try / catch
projects, err := runADOProjects(cmd, args)
if err != nil && strings.Contains(err.Error(), "failed to list projects") {
if isAuthError(err) {
// regenerate PAT and retry once
}
if isTimeout(err) { /* backoff + retry */ }
return fmt.Errorf("ado list failed: %w", err)
} Prevention
- Rotate PATs before expiry and store them in a secret manager.
- Grant the PAT Project and Team (Read) scope.
- Verify org name and network egress (HTTPS_PROXY) in CI before sync jobs.
- Handle transient 5xx with backoff rather than treating as fatal.
When it happens
Trigger: Any ListProjects call failing: expired or unauthorized PAT (HTTP 401/203), PAT lacking scope, wrong org name (404), DNS/proxy failure, or a mock server in tests returning an error (TestADOProjectsHTTPError).
Common situations: PAT rotated or revoked in Azure DevOps; org renamed; corporate proxy blocking dev.azure.com; CI runner without network egress; typo in org so the endpoint 404s.
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
- pypi api returned status %d
- transient error %d (attempt %d/%d)
- max retries (%d) exceeded: %w
- failed to fetch work items: %w
- failed to add work item link: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/f4d213e96ac465f9.
Report an issue: GitHub.