kubernetes/kops · error
failed to delete load balancer with name %s %v
Error message
failed to delete load balancer with name %s %v
What it means
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.
Source
Thrown at pkg/resources/digitalocean/resources.go:322
func deleteRecord(cloud fi.Cloud, domain string, t *resources.Resource) error {
c := cloud.(do.DOCloud)
record := t.Obj.(godo.DomainRecord)
_, err := c.DomainService().DeleteRecord(context.TODO(), domain, record.ID)
if err != nil {
return fmt.Errorf("failed to delete record for domain %s: %d", domain, record.ID)
}
return nil
}
func deleteLoadBalancer(cloud fi.Cloud, t *resources.Resource) error {
c := cloud.(do.DOCloud)
lb := t.Obj.(godo.LoadBalancer)
_, err := c.LoadBalancersService().Delete(context.TODO(), lb.ID)
if err != nil {
return fmt.Errorf("failed to delete load balancer with name %s %v", lb.Name, err)
}
return nil
}
func waitForDetach(cloud do.DOCloud, action *godo.Action) error {
timeout := time.After(10 * time.Second)
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-timeout:
return errors.New("timed out waiting for volume to detach")
case <-ticker.C:
updatedAction, _, err := cloud.ActionsService().Get(context.TODO(), action.ID)
if err != nil {
return err
}View on GitHub (pinned to 4c8573c808)
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
Example fix
// before
if err != nil {
return fmt.Errorf("failed to delete load balancer with name %s %v", lb.Name, err)
}
// after
if err != nil {
if gerr, ok := err.(*godo.ErrorResponse); ok && gerr.Response.StatusCode == http.StatusNotFound {
return nil
}
return fmt.Errorf("failed to delete load balancer %q (ID %s): %v", lb.Name, lb.ID, err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Confirm the load balancer exists before deleting
_, resp, err := c.LoadBalancersService().Get(context.TODO(), lb.ID)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
return nil
}
return fmt.Errorf("cannot fetch load balancer %s: %v", lb.ID, err)
} Try / catch
_, err := c.LoadBalancersService().Delete(context.TODO(), lb.ID)
if err != nil {
if gerr, ok := err.(*godo.ErrorResponse); ok && gerr.Response.StatusCode == http.StatusNotFound {
return nil
}
if gerr, ok := err.(*godo.ErrorResponse); ok && (gerr.Response.StatusCode == 429 || gerr.Response.StatusCode >= 500) {
// retry with backoff
}
return fmt.Errorf("failed to delete load balancer %q (ID %s): %v", lb.Name, lb.ID, err)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- failed to list lbs: %v
- failed to delete record for domain %s: %d
- failed to delete ssh key %s (ID %s): %s
- failed to apply resource record set: %s, err: %s
- unhandled LoadBalancer type %q
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/11247933f1b9eefe.
Report an issue: GitHub.