{"record":{"id":"3b65e4572b8d2bd8","repo":"kubernetes/kops","slug":"failed-to-delete-record-v","errorCode":null,"errorMessage":"failed to delete record: %v","messagePattern":"failed to delete record: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"dnsprovider/pkg/dnsprovider/providers/do/dns.go","lineNumber":365,"sourceCode":"\t\t\t\treturn fmt.Errorf(\"failed to apply resource record set: %s, err: %s\", rrset.Name(), err)\n\t\t\t}\n\t\t}\n\n\t\tklog.V(2).Info(\"record change set upserts complete\")\n\t}\n\n\tif len(r.removals) > 0 {\n\t\trecords, err := getRecords(r.client, r.zone.Name())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, record := range r.removals {\n\t\t\tfor _, domainRecord := range records {\n\t\t\t\tif domainRecord.Name == record.Name() {\n\t\t\t\t\terr := deleteRecord(r.client, r.zone.Name(), domainRecord.ID)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"failed to delete record: %v\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tklog.V(2).Info(\"record change set removals complete\")\n\t}\n\n\tklog.V(2).Info(\"record change sets successfully applied\")\n\treturn nil\n}\n\n// IsEmpty returns true if a changeset is empty, false otherwise\nfunc (r *resourceRecordChangeset) IsEmpty() bool {\n\tif len(r.additions) == 0 && len(r.removals) == 0 && len(r.upserts) == 0 {\n\t\treturn true\n\t}\n","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/dnsprovider/pkg/dnsprovider/providers/do/dns.go#L347-L383","documentation":"In Apply()'s removals phase, each planned removal is matched against the fetched zone records by name and deleted via deleteRecord. If the DO DELETE call fails, this error wraps the cause. Note the loop continues matching other records even after a failure returns immediately — the first deletion error aborts Apply.","triggerScenarios":"Apply() with removals where deleteRecord(client, zone.Name(), domainRecord.ID) returns an error — record already deleted (404), domain missing (404 project/domain), token without write scope, or 429 rate limiting.","commonSituations":"External controllers deleted the record between List and Apply (404 on DELETE); zone/domain removed from DO account; read-only API token; burst of removals hitting DO rate limits.","solutions":["Check the wrapped error; treat 404 as record-already-deleted and make deletion idempotent in the caller or retry the Apply.","Confirm the domain still exists in DO (`doctl compute domain list`).","Use a token with write scope for the domain.","Back off and retry on 429 rate-limit responses."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// confirm zone still exists before planning removals\n_, resp, err := doClient.Domains.Get(ctx, strings.TrimSuffix(zone, \".\"))\nif err != nil || resp.StatusCode == 404 {\n    return errors.New(\"DO domain no longer exists; skip removals\")\n}","typeGuard":null,"tryCatchPattern":"err := recordSet.Apply()\nif err != nil && strings.Contains(err.Error(), \"failed to delete record\") {\n    if strings.Contains(err.Error(), \"404\") {\n        // already deleted elsewhere — safe to proceed / retry idempotently\n        return nil\n    }\n    return err\n}","preventionTips":["Treat delete-404 as success to keep Apply idempotent.","Use a write-scoped DO token.","Back off on 429s when removing many records in bulk."],"tags":["digitalocean","dns","delete"],"backgroundTag":"dns-record-update-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"}