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

  1. Read the wrapped %v error for the DO status code; on 404 the LB is already gone — rerun kOps delete cluster to complete cleanup
  2. Verify the API token has write permissions and is not expired (doctl auth list)
  3. If the LB is locked, detach/resolve dependent resources (e.g. certificates, droplets) then delete again
  4. 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

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


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/11247933f1b9eefe. Report an issue: GitHub.