{"record":{"id":"11247933f1b9eefe","repo":"kubernetes/kops","slug":"failed-to-delete-load-balancer-with-name-s-v","errorCode":null,"errorMessage":"failed to delete load balancer with name %s %v","messagePattern":"failed to delete load balancer with name (.+?) (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/resources/digitalocean/resources.go","lineNumber":322,"sourceCode":"\nfunc deleteRecord(cloud fi.Cloud, domain string, t *resources.Resource) error {\n\tc := cloud.(do.DOCloud)\n\trecord := t.Obj.(godo.DomainRecord)\n\n\t_, err := c.DomainService().DeleteRecord(context.TODO(), domain, record.ID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete record for domain %s: %d\", domain, record.ID)\n\t}\n\n\treturn nil\n}\n\nfunc deleteLoadBalancer(cloud fi.Cloud, t *resources.Resource) error {\n\tc := cloud.(do.DOCloud)\n\tlb := t.Obj.(godo.LoadBalancer)\n\t_, err := c.LoadBalancersService().Delete(context.TODO(), lb.ID)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete load balancer with name %s %v\", lb.Name, err)\n\t}\n\n\treturn nil\n}\n\nfunc waitForDetach(cloud do.DOCloud, action *godo.Action) error {\n\ttimeout := time.After(10 * time.Second)\n\tticker := time.NewTicker(500 * time.Millisecond)\n\tdefer ticker.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-timeout:\n\t\t\treturn errors.New(\"timed out waiting for volume to detach\")\n\t\tcase <-ticker.C:\n\t\t\tupdatedAction, _, err := cloud.ActionsService().Get(context.TODO(), action.ID)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/resources/digitalocean/resources.go#L304-L340","documentation":"deleteLoadBalancer wraps an error from the DigitalOcean LoadBalancers Delete API when removing a load balancer by ID during cluster teardown. Unlike error 1680, the underlying error is included via %v, so the DO API message is visible. Thrown only when the DO LoadBalancer delete call fails.","triggerScenarios":"godo LoadBalancersService().Delete fails: load balancer already deleted (404), token lacks write scope (401/403), LB ID invalid, rate limit (429), or transient network/5xx error.","commonSituations":"Re-running kOps delete cluster after a partial failure so the LB was already removed; DO account in a state where LBs are locked (droplets still attached / cert in use); expired API token; region outage causing 5xx.","solutions":["Read the wrapped %v error for the DO status code; on 404 the LB is already gone — rerun kOps delete cluster to complete cleanup","Verify the API token has write permissions and is not expired (doctl auth list)","If the LB is locked, detach/resolve dependent resources (e.g. certificates, droplets) then delete again","Retry after backoff on 429/5xx responses"],"exampleFix":"// before\nif err != nil {\n\treturn fmt.Errorf(\"failed to delete load balancer with name %s %v\", lb.Name, err)\n}\n// after\nif err != nil {\n\tif gerr, ok := err.(*godo.ErrorResponse); ok && gerr.Response.StatusCode == http.StatusNotFound {\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"failed to delete load balancer %q (ID %s): %v\", lb.Name, lb.ID, err)\n}","handlingStrategy":"try-catch","validationCode":"// Confirm the load balancer exists before deleting\n_, resp, err := c.LoadBalancersService().Get(context.TODO(), lb.ID)\nif err != nil {\n\tif resp != nil && resp.StatusCode == http.StatusNotFound {\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"cannot fetch load balancer %s: %v\", lb.ID, err)\n}","typeGuard":null,"tryCatchPattern":"_, err := c.LoadBalancersService().Delete(context.TODO(), lb.ID)\nif err != nil {\n\tif gerr, ok := err.(*godo.ErrorResponse); ok && gerr.Response.StatusCode == http.StatusNotFound {\n\t\treturn nil\n\t}\n\tif gerr, ok := err.(*godo.ErrorResponse); ok && (gerr.Response.StatusCode == 429 || gerr.Response.StatusCode >= 500) {\n\t\t// retry with backoff\n\t}\n\treturn fmt.Errorf(\"failed to delete load balancer %q (ID %s): %v\", lb.Name, lb.ID, err)\n}","preventionTips":["Ensure the DO token has load-balancer write scope before teardown","Handle 404 as success for idempotent deletes","Retry 429/5xx with exponential backoff","Check DO status page during region-wide failures"],"tags":["digitalocean","load-balancer","api-error","destructive-ops"],"backgroundTag":"cloud-resource-delete-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"}