{"record":{"id":"24487b2a1920f855","repo":"TechnitiumSoftware/DnsServer","slug":"cannot-delete-soa-record-24487b","errorCode":null,"errorMessage":"Cannot delete SOA record.","messagePattern":"Cannot delete SOA record\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Zones/PrimaryZone.cs","lineNumber":2683,"sourceCode":"\n                        if (_dnssecStatus != AuthZoneDnssecStatus.Unsigned)\n                            UpdateDnssecRecordsFor(this, record.Type);\n\n                        TriggerNotify();\n\n                        return true;\n                    }\n\n                    return false;\n            }\n        }\n\n        public override bool DeleteRecords(DnsResourceRecordType type)\n        {\n            switch (type)\n            {\n                case DnsResourceRecordType.SOA:\n                    throw new InvalidOperationException(\"Cannot delete SOA 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 delete DNSSEC records.\");\n\n                default:\n                    if (_entries.TryRemove(type, out IReadOnlyList<DnsResourceRecord> removedRecords))\n                    {\n                        CommitAndIncrementSerial(removedRecords);\n\n                        if (_dnssecStatus != AuthZoneDnssecStatus.Unsigned)\n                            UpdateDnssecRecordsFor(this, type);\n\n                        TriggerNotify();\n","sourceCodeStart":2665,"sourceCodeEnd":2701,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Zones/PrimaryZone.cs#L2665-L2701","documentation":"Thrown by PrimaryZone.DeleteRecords when the requested type is DnsResourceRecordType.SOA. The SOA record is mandatory and defines zone authority, so deleting the entire RRset is treated as a programming error and surfaced as an InvalidOperationException rather than a data validation failure.","triggerScenarios":"Calling zone.DeleteRecords(DnsResourceRecordType.SOA) on any PrimaryZone instance. The switch matches the SOA case before the default branch.","commonSituations":"Generic cleanup loops that iterate all record types and call DeleteRecords for each; bulk-delete tooling that does not exclude SOA/NS; refactoring a record-wipe routine.","solutions":["Exclude DnsResourceRecordType.SOA from the set of types passed to DeleteRecords.","Use SetRecords(SOA, newRecords) to replace the SOA RRset instead of deleting it.","Skip SOA in any 'clear all records' helper."],"exampleFix":"// before\nforeach (var type in allTypes)\n    zone.DeleteRecords(type);\n\n// after\nforeach (var type in allTypes.Where(t => t != DnsResourceRecordType.SOA))\n    zone.DeleteRecords(type);","handlingStrategy":"type-guard","validationCode":"var protectedTypes = new[] { DnsResourceRecordType.SOA };\nforeach (var type in types.Except(protectedTypes))\n    zone.DeleteRecords(type);","typeGuard":"static bool IsDeletableRrsetType(DnsResourceRecordType t) =>\n    t != DnsResourceRecordType.SOA;","tryCatchPattern":"null","preventionTips":["Maintain a single 'protected types' set covering SOA and DNSSEC used by all delete calls.","Replace SOA via SetRecords rather than delete.","Unit-test bulk-delete helpers against a zone that contains every record type."],"tags":["dns","primary-zone","soa","immutable-record","delete"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}