kubernetes/kops · error
failed to delete record for domain %s: %d
Error message
failed to delete record for domain %s: %d
What it means
deleteRecord wraps any error from the DigitalOcean Domains.DeleteRecord API (deleting a DNS record by domain name and record ID). The wrapper discards the underlying error and only formats the domain and record ID, so the real cause (network, auth, not-found, rate limit) is lost. It is thrown during kOps cluster teardown while removing cluster DNS records.
Source
Thrown at pkg/resources/digitalocean/resources.go:311
return fmt.Errorf("error while waiting for volume %s to detach: %s", volume.ID, err)
}
}
_, err := c.VolumeService().DeleteVolume(context.TODO(), t.ID)
if err != nil {
return fmt.Errorf("failed to delete volume: %s, err: %s", t.ID, err)
}
return nil
}
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)View on GitHub (pinned to 4c8573c808)
Solutions
- Check the DO API token (doctl auth list / DIGITALOCEAN_ACCESS_TOKEN) is valid and has write scope, then retry the delete
- List existing records for the domain (doctl compute domain records list <domain>) to confirm the record still exists; if already gone, rerun kOps delete and the tracker is refreshed
- Check DO API status/rate limits if errors are 429, and retry after backoff
- Improve the error message to include the wrapped err (see exampleFix) to diagnose future occurrences
Example fix
// before
return fmt.Errorf("failed to delete record for domain %s: %d", domain, record.ID)
// after
return fmt.Errorf("failed to delete record %d for domain %s: %v", record.ID, domain, err) Defensive patterns
Strategy: try-catch
Validate before calling
// Before deleting, verify the record still exists and the token works
records, _, err := c.DomainService().Records(context.TODO(), domain)
if err != nil {
return fmt.Errorf("cannot list records for domain %s: %v", domain, err)
}
found := false
for _, r := range records {
if r.ID == record.ID {
found = true
}
}
if !found {
klog.V(2).Infof("record %d already gone for domain %s", record.ID, domain)
return nil
} Try / catch
// Wrap and preserve cause; tolerate already-deleted
_, err := c.DomainService().DeleteRecord(context.TODO(), domain, record.ID)
if err != nil {
if gerr, ok := err.(*godo.ErrorResponse); ok && gerr.Response.StatusCode == http.StatusNotFound {
return nil
}
return fmt.Errorf("failed to delete record %d for domain %s: %v", record.ID, domain, err)
} Prevention
- Always wrap API errors with %v/%w so root causes are diagnosable
- Treat HTTP 404 as success in idempotent delete paths
- Verify API token validity/scope before starting cluster teardown
- Retry delete operations with backoff; they are idempotent
When it happens
Trigger: godo DomainService().DeleteRecord returns any error: record already deleted (404), invalid OAuth token (401), domain no longer exists (404), rate limiting (429), or network failure.
Common situations: Retrying a partially-failed cluster delete so the DNS record was already removed; expired/revoked doctl API token; domain zone deleted out-of-band before record deletion; DO API rate limits during large teardowns.
Related errors
- failed to apply resource record set: %s, err: %s
- failed to delete load balancer with name %s %v
- failed to delete ssh key %s (ID %s): %s
- failed to delete record: %v
- failed to list domains: %s
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/fa7ab2c8f40cebe1.
Report an issue: GitHub.