{"record":{"id":"be63b1057f9eca61","repo":"TechnitiumSoftware/DnsServer","slug":"cannot-update-record-use-setrecords-for-type-be63b1","errorCode":null,"errorMessage":"Cannot update record: use SetRecords() for {type} record","messagePattern":"Cannot update record: use SetRecords\\(\\) for (.+?) record","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Zones/PrimaryZone.cs","lineNumber":2745,"sourceCode":"\n                        if (_dnssecStatus != AuthZoneDnssecStatus.Unsigned)\n                            UpdateDnssecRecordsFor(this, type);\n\n                        TriggerNotify();\n\n                        return true;\n                    }\n\n                    return false;\n            }\n        }\n\n        public override void UpdateRecord(DnsResourceRecord oldRecord, DnsResourceRecord newRecord)\n        {\n            switch (oldRecord.Type)\n            {\n                case DnsResourceRecordType.SOA:\n                    throw new InvalidOperationException(\"Cannot update record: use SetRecords() for \" + oldRecord.Type.ToString() + \" record\");\n\n                case DnsResourceRecordType.DNSKEY:\n                case DnsResourceRecordType.RRSIG:\n                case DnsResourceRecordType.NSEC:\n                case DnsResourceRecordType.NSEC3PARAM:\n                case DnsResourceRecordType.NSEC3:\n                    throw new InvalidOperationException(\"Cannot update DNSSEC records.\");\n\n                default:\n                    if (oldRecord.Type != newRecord.Type)\n                        throw new InvalidOperationException(\"Old and new record types do not match.\");\n\n                    if ((_dnssecStatus != AuthZoneDnssecStatus.Unsigned) && newRecord.GetAuthGenericRecordInfo().Disabled)\n                        throw new DnsServerException(\"Cannot update record: disabling records in a signed zones is not supported.\");\n\n                    if (newRecord.OriginalTtlValue > GetZoneSoaExpire())\n                        throw new DnsServerException(\"Cannot update record: TTL cannot be greater than SOA EXPIRE.\");\n","sourceCodeStart":2727,"sourceCodeEnd":2763,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Zones/PrimaryZone.cs#L2727-L2763","documentation":"Thrown by PrimaryZone.UpdateRecord when oldRecord.Type is SOA. Because a zone has exactly one SOA and it carries serial/timing fields, UpdateRecord refuses it and points the caller to SetRecords(), which replaces the whole SOA RRset atomically. InvalidOperationException signals the wrong API was chosen.","triggerScenarios":"Calling zone.UpdateRecord(oldSoaRecord, newSoaRecord) on a PrimaryZone.","commonSituations":"Generic 'update record' UI that routes all edits through UpdateRecord; SOA serial-bump logic that tries to mutate the SOA in place.","solutions":["Route SOA edits through zone.SetRecords(DnsResourceRecordType.SOA, new[] { newSoaRecord }).","In a generic editor, branch on type: SOA -> SetRecords, else UpdateRecord.","When bumping the serial, build a new SOA RRset and call SetRecords."],"exampleFix":"// before\nzone.UpdateRecord(oldSoa, newSoa);\n\n// after\nzone.SetRecords(DnsResourceRecordType.SOA, new[] { newSoa });","handlingStrategy":"validation","validationCode":"if (oldRecord.Type == DnsResourceRecordType.SOA)\n    zone.SetRecords(DnsResourceRecordType.SOA, new[] { newRecord });\nelse\n    zone.UpdateRecord(oldRecord, newRecord);","typeGuard":"static bool RequiresSetRecords(DnsResourceRecordType t) =>\n    t == DnsResourceRecordType.SOA;","tryCatchPattern":"null","preventionTips":["Make generic record editors branch SOA to SetRecords.","Build SOA serial bumps by replacing the full RRset."],"tags":["dns","primary-zone","soa","immutable-record","update"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}