{"record":{"id":"8bf84b57bcfcebb6","repo":"kubernetes/kops","slug":"loadbalancers-list-returned-error-v","errorCode":null,"errorMessage":"LoadBalancers.List returned error: %v","messagePattern":"LoadBalancers\\.List returned error: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/do/cloud.go","lineNumber":280,"sourceCode":"\t\treturn \"\", err\n\t}\n\n\tif done {\n\t\treturn vpcUUID, nil\n\t} else {\n\t\treturn \"\", wait.ErrWaitTimeout\n\t}\n}\n\nfunc (c *doCloudImplementation) GetApiIngressStatus(cluster *kops.Cluster) ([]fi.ApiIngressStatus, error) {\n\tvar ingresses []fi.ApiIngressStatus\n\tdone, err := vfs.RetryWithBackoff(readBackoff, func() (bool, error) {\n\t\t// Note that this must match Digital Ocean's lb name\n\t\tklog.V(2).Infof(\"Querying DO to find Loadbalancers for API (%q)\", cluster.Name)\n\n\t\tloadBalancers, err := c.GetAllLoadBalancers()\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"LoadBalancers.List returned error: %v\", err)\n\t\t}\n\n\t\tlbName := \"api-\" + strings.ReplaceAll(cluster.Name, \".\", \"-\")\n\n\t\tfor _, lb := range loadBalancers {\n\t\t\tif lb.Name == lbName {\n\t\t\t\tklog.V(10).Infof(\"Matching LB name found for API (%q)\", cluster.Name)\n\n\t\t\t\tif lb.Status != \"active\" {\n\t\t\t\t\treturn false, fmt.Errorf(\"load-balancer is not yet active (current status: %s)\", lb.Status)\n\t\t\t\t}\n\n\t\t\t\taddress := lb.IP\n\t\t\t\tingresses = append(ingresses, fi.ApiIngressStatus{IP: address})\n\t\t\t}\n\t\t}\n\t\treturn true, nil\n\t})","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/do/cloud.go#L262-L298","documentation":"GetApiIngressStatus retries (vfs.RetryWithBackoff) listing DigitalOcean load balancers via c.GetAllLoadBalancers() to find the API load balancer named 'api-<cluster-name>'. If the list call itself fails, the error is wrapped with 'LoadBalancers.List returned error'. This is a read-path failure, not a load balancer state problem.","triggerScenarios":"GetAllLoadBalancers() (backed by godo LoadBalancers.List) fails due to API auth errors, rate limiting (429), network failures, or region-scoped API errors while kOps is trying to report the API server ingress address.","commonSituations":"Expired/invalid DO API token; hitting DO rate limits during heavy cluster operations; transient network outages between the kOps host and DO; using kOps from an environment without egress to api.digitalocean.com.","solutions":["Check the wrapped error: 401/403 means fix the DO API token scopes; 429 means back off and retry later.","Verify network egress to api.digitalocean.com from the machine running kOps.","Re-run the command — RetryWithBackoff already retries transient failures; persistent failure indicates auth or connectivity.","Confirm the DO account/team the token belongs to actually owns the load balancer (token scoped to wrong team returns empty/forbidden lists)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// verify connectivity and token before listing\n_, _, err := client.LoadBalancers.List(context.TODO(), nil)\nif err != nil { return fmt.Errorf(\"DO API unreachable or unauthorized: %w\", err) }","typeGuard":null,"tryCatchPattern":"ingress, err := cloud.GetApiIngressStatus(cluster)\nif err != nil {\n    if isRateLimited(err) { backoff(); retry() }\n    return fmt.Errorf(\"DO LB list failed: %w\", err)\n}","preventionTips":["Ensure valid, correctly scoped DO API token.","Respect DO rate limits; avoid tight polling loops.","Verify egress to api.digitalocean.com from CI/kOps hosts.","Ensure the token belongs to the team owning the load balancer."],"tags":["digitalocean","loadbalancer","api","network"],"backgroundTag":"cloud-api-list-failed","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}