{"record":{"id":"729d3fe59a6ec023","repo":"kubernetes/kops","slug":"failed-to-apply-resource-record-set-s-err-s","errorCode":null,"errorMessage":"failed to apply resource record set: %s, err: %s","messagePattern":"failed to apply resource record set: (.+?), err: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"dnsprovider/pkg/dnsprovider/providers/do/dns.go","lineNumber":336,"sourceCode":"\treturn r\n}\n\n// Apply adds new records stored in r.additions, updates records stored\n// in r.upserts and deletes records stored in r.removals\nfunc (r *resourceRecordChangeset) Apply(ctx context.Context) error {\n\t// Empty changesets should be a relatively quick no-op\n\tif r.IsEmpty() {\n\t\tklog.V(4).Info(\"record change set is empty\")\n\t\treturn nil\n\t}\n\n\tklog.V(2).Info(\"applying changes in record change set\")\n\n\tif len(r.additions) > 0 {\n\t\tfor _, rrset := range r.additions {\n\t\t\terr := r.applyResourceRecordSet(rrset)\n\t\t\tif err != nil {\n\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 additions complete\")\n\t}\n\n\tif len(r.upserts) > 0 {\n\t\tfor _, rrset := range r.upserts {\n\t\t\terr := r.applyResourceRecordSet(rrset)\n\t\t\tif err != nil {\n\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 {","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/dnsprovider/pkg/dnsprovider/providers/do/dns.go#L318-L354","documentation":"DigitalOcean DNS Apply() iterates the additions portion of a record change set, calling applyResourceRecordSet for each new record. If creating any record fails, the record name and underlying error are wrapped here and Apply aborts.","triggerScenarios":"Apply() with non-empty additions where applyResourceRecordSet fails — e.g. POST to /v2/domains/{zone}/records returns 4xx/5xx, domain doesn't exist, invalid record data/TTL, or DO API token invalid.","commonSituations":"The DO domain (zone) was deleted or the zone name includes the trailing dot mismatch; invalid MX/CNAME target; API token lacking write scope; DO API rate limiting (429).","solutions":["Check the wrapped err from the DO API response for the specific cause (404 domain, 422 validation, 401 token).","Verify the domain exists in DO: `doctl compute domain list`.","Validate the record's type/name/data/TTL are accepted by the DO API.","Regenerate the DO API token if auth errors occur; back off on 429s."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-flight: domain must exist and token must work\nresp, err := http.Get(\"https://api.digitalocean.com/v2/domains/\" + strings.TrimSuffix(zone, \".\"))\n// with Authorization: Bearer <token>; a 404 here predicts Apply failure\nif err != nil || resp.StatusCode == http.StatusNotFound {\n    return errors.New(\"DO domain missing or token invalid\")\n}","typeGuard":null,"tryCatchPattern":"err := recordSet.Apply()\nif err != nil && strings.Contains(err.Error(), \"failed to apply resource record set\") {\n    var apiErr *godo.ErrorResponse\n    if errors.As(err, &apiErr) && apiErr.Response.StatusCode == 429 {\n        time.Sleep(backoff) // rate limited, retry\n    }\n    return fmt.Errorf(\"DO record create failed: %w\", err)\n}","preventionTips":["Create the DO domain before applying records.","Use a token with write scope; rotate before expiry.","Validate record data/TTL against DO API constraints beforehand."],"tags":["digitalocean","dns","api-error"],"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"}