{"record":{"id":"f4d213e96ac465f9","repo":"gastownhall/beads","slug":"failed-to-list-projects-w","errorCode":null,"errorMessage":"failed to list projects: %w","messagePattern":"failed to list projects: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/ado.go","lineNumber":442,"sourceCode":"\n\tcfg := getADOConfig()\n\tif cfg.PAT == \"\" {\n\t\treturn fmt.Errorf(\"ado.pat not configured: set via 'bd config set ado.pat <token>' or AZURE_DEVOPS_PAT env var\")\n\t}\n\tif cfg.Org == \"\" && cfg.URL == \"\" {\n\t\treturn fmt.Errorf(\"ado.org not configured: set via 'bd config set ado.org <org>' or AZURE_DEVOPS_ORG env var\")\n\t}\n\n\tout := cmd.OutOrStdout()\n\tclient, err := getADOClient(cfg)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid ADO configuration: %w\", err)\n\t}\n\tctx := context.Background()\n\n\tprojects, err := client.ListProjects(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to list projects: %w\", err)\n\t}\n\n\tif jsonOutput {\n\t\treturn outputJSON(projects)\n\t}\n\n\t_, _ = fmt.Fprintln(out, \"Azure DevOps Projects\")\n\t_, _ = fmt.Fprintln(out, \"=====================\")\n\tfor _, p := range projects {\n\t\t_, _ = fmt.Fprintf(out, \"  %s\\n\", p.Name)\n\t\tif p.Description != \"\" {\n\t\t\t_, _ = fmt.Fprintf(out, \"    %s\\n\", p.Description)\n\t\t}\n\t}\n\n\tif len(projects) == 0 {\n\t\t_, _ = fmt.Fprintln(out, \"No projects found\")\n\t}","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/ado.go#L424-L460","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nError: failed to list projects: ADO API error: TF400813 Resource unavailable: unauthorized\n\n// after\n$ bd config set ado.pat <new-pat-with-read-scope>\n$ bd ado projects","handlingStrategy":"try-catch","validationCode":"if os.Getenv(\"AZURE_DEVOPS_PAT\") == \"\" && cfg.PAT == \"\" {\n\treturn errors.New(\"PAT not configured; skipping ADO call\")\n}\n// optional pre-flight:\nresp, err := http.Head(\"https://dev.azure.com/\" + org)\nif err != nil || resp.StatusCode >= 500 { return fmt.Errorf(\"ADO unreachable\") }","typeGuard":"func isAuthError(err error) bool {\n\treturn err != nil && (strings.Contains(err.Error(), \"401\") || strings.Contains(err.Error(), \"unauthorized\"))\n}","tryCatchPattern":"projects, err := runADOProjects(cmd, args)\nif err != nil && strings.Contains(err.Error(), \"failed to list projects\") {\n\tif isAuthError(err) {\n\t\t// regenerate PAT and retry once\n\t}\n\tif isTimeout(err) { /* backoff + retry */ }\n\treturn fmt.Errorf(\"ado list failed: %w\", err)\n}","preventionTips":["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."],"tags":["azure-devops","http","api-error","network"],"backgroundTag":"http-request-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}