{"record":{"id":"8758aa3718c71195","repo":"TechnitiumSoftware/DnsServer","slug":"cannot-convert-to-nsec-the-zone-must-be-signed-wi","errorCode":null,"errorMessage":"Cannot convert to NSEC: the zone must be signed with NSEC3 for conversion.","messagePattern":"Cannot convert to NSEC: the zone must be signed with NSEC3 for conversion\\.","errorType":"exception","errorClass":"DnsServerException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Zones/PrimaryZone.cs","lineNumber":698,"sourceCode":"            {\n                lock (dnssecTimer)\n                {\n                    dnssecTimer.Dispose();\n                    _dnssecTimer = null;\n                }\n            }\n\n            _dnssecPrivateKeys = null;\n            _dnssecStatus = AuthZoneDnssecStatus.Unsigned;\n\n            CommitAndIncrementSerial(deletedRecords);\n            TriggerNotify();\n        }\n\n        public void ConvertToNSec()\n        {\n            if (_dnssecStatus != AuthZoneDnssecStatus.SignedWithNSEC3)\n                throw new DnsServerException(\"Cannot convert to NSEC: the zone must be signed with NSEC3 for conversion.\");\n\n            lock (_dnssecUpdateLock)\n            {\n                IReadOnlyList<AuthZone> zones = _dnsServer.AuthZoneManager.GetApexZoneWithSubDomainZones(_name);\n\n                DisableNSec3(zones);\n\n                //since zones were removed when disabling NSEC3; get updated non empty zones list\n                List<AuthZone> nonEmptyZones = new List<AuthZone>(zones.Count);\n\n                foreach (AuthZone zone in zones)\n                {\n                    if (!zone.IsEmpty)\n                        nonEmptyZones.Add(zone);\n                }\n\n                EnableNSec(nonEmptyZones);\n","sourceCodeStart":680,"sourceCodeEnd":716,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Zones/PrimaryZone.cs#L680-L716","documentation":"Thrown by PrimaryZone.ConvertToNSec when _dnssecStatus != AuthZoneDnssecStatus.SignedWithNSEC3. Conversion is only defined from NSEC3 to NSEC; calling it on an Unsigned zone or an NSEC-signed zone is invalid. The guard raises DnsServerException before acquiring _dnssecUpdateLock or touching zone data.","triggerScenarios":"zone.ConvertToNSec() on a zone that is Unsigned or SignedWithNSEC.","commonSituations":"Calling ConvertToNSec on a freshly signed NSEC zone by mistake; UI offering the conversion unconditionally; running the conversion in a sequence without verifying the prior sign mode.","solutions":["Check zone.DnssecStatus == SignedWithNSEC3 before calling ConvertToNSec.","If the zone is Unsigned, sign with useNSec3=false directly instead of converting.","If NSEC-signed already, no conversion is needed."],"exampleFix":"// before\nzone.ConvertToNSec(); // throws unless NSEC3-signed\n\n// after\nif (zone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3)\n    zone.ConvertToNSec();","handlingStrategy":"try-catch","validationCode":"if (zone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3)\n    zone.ConvertToNSec();","typeGuard":"static bool CanConvertToNsec(AuthZone z) => z.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3;","tryCatchPattern":"try { zone.ConvertToNSec(); }\ncatch (DnsServerException ex) when (ex.Message.Contains(\"must be signed with NSEC3\"))\n{ /* wrong sign mode: sign with NSEC3 first, or no-op if already NSEC */ }","preventionTips":["Only call ConvertToNSec on NSEC3-signed zones.","For unsigned zones, sign with useNSec3=false instead of converting.","Verify DnssecStatus before any NSEC/NSEC3 conversion."],"tags":["csharp","dns","dnssec","nsec3","zone-management","technitium-dns"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}