kubernetes/kops · error
failed to delete VPC %s (ID %s): %s
Error message
failed to delete VPC %s (ID %s): %s
What it means
deleteVPC wraps the godo VPCsService.Delete failure during DigitalOcean cluster teardown. It fires when the DO API refuses to delete the cluster's VPC (non-empty, permission denied, or transient API error); the VPC name and ID are echoed for correlation.
Source
Thrown at pkg/resources/digitalocean/resources.go:273
c := cloud.(do.DOCloud)
dropletID, err := strconv.Atoi(t.ID)
if err != nil {
return fmt.Errorf("failed to convert droplet ID to int: %s", err)
}
_, err = c.DropletsService().Delete(context.TODO(), dropletID)
if err != nil {
return fmt.Errorf("failed to delete droplet: %d, err: %s", dropletID, err)
}
return nil
}
func deleteVPC(cloud fi.Cloud, t *resources.Resource) error {
c := cloud.(do.DOCloud)
_, err := c.VPCsService().Delete(context.TODO(), t.ID)
if err != nil {
return fmt.Errorf("failed to delete VPC %s (ID %s): %s", t.Name, t.ID, err)
}
return nil
}
func deleteVolume(cloud fi.Cloud, t *resources.Resource) error {
c := cloud.(do.DOCloud)
volume := t.Obj.(godo.Volume)
for _, dropletID := range volume.DropletIDs {
action, resp, err := c.VolumeActionService().DetachByDropletID(context.TODO(), volume.ID, dropletID)
if err != nil {
if resp != nil && resp.StatusCode == http.StatusNotFound {
// Volume is already detached, nothing to do.
continue
}
return fmt.Errorf("failed to detach volume %s: %s", volume.ID, err)
}
View on GitHub (pinned to 4c8573c808)
Solutions
- Check the DigitalOcean API token and VPC delete permissions
- Ensure no droplets/resources remain in the VPC, then retry
- Verify the VPC ID still exists; 404 means already deleted
Example fix
// before # VPC delete rejected: member droplets still attached // after kops delete cluster --cloud digitalocean ... # removes droplets before VPC; or doctl compute droplet delete <id> first
Defensive patterns
Strategy: retry
Validate before calling
vpcs, _, _ := c.VPCsService().List(context.TODO(), &godo.ListOptions{})
for _, v := range vpcs {
if v.ID == vpcID && v.Default {
return errors.New("cannot delete the default VPC")
}
} Try / catch
if _, err := c.VPCsService().Delete(context.TODO(), t.ID); err != nil {
var gerr *godo.ErrorResponse
if errors.As(err, &gerr) && gerr.Response.StatusCode == http.StatusNotFound {
return nil // already deleted
}
return fmt.Errorf("failed to delete VPC %s (ID %s): %w", t.Name, t.ID, err)
} Prevention
- Delete all member resources (droplets, LBs) before deleting the VPC
- Never target the region default VPC
- Treat 404 as idempotent success
- Check token vpc write scope
When it happens
Trigger: VPC delete API returns 404 (already deleted), 409/422 (VPC still in use — resources like droplets/LBs remain attached), 403 (scope), or transient API error.
Common situations: Deleting a VPC that DO does not allow removing because member resources still exist; VPC deleted manually earlier; default VPC cannot be deleted; token scope too narrow.
Related errors
- failed to apply resource record set: %s, err: %s
- error listing subnets: %v
- failed to list lbs: %v
- failed to delete droplet: %d, err: %s
- failed to delete volume: %s, err: %s
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/c9c6f8cce6d63d18.
Report an issue: GitHub.