{"record":{"id":"21f94ba96a8cce25","repo":"TechnitiumSoftware/DnsServer","slug":"cannot-delete-private-key-only-keys-with-generate","errorCode":null,"errorMessage":"Cannot delete private key: only keys with Generated state can be deleted.","messagePattern":"Cannot delete private key: only keys with Generated state can be deleted\\.","errorType":"exception","errorClass":"DnsServerException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Zones/PrimaryZone.cs","lineNumber":1099,"sourceCode":"\n                privateKey.RolloverDays = rolloverDays;\n\n                return privateKey;\n            }\n        }\n\n        public void DeletePrivateKey(ushort keyTag)\n        {\n            if (_dnssecStatus == AuthZoneDnssecStatus.Unsigned)\n                throw new DnsServerException(\"The zone must be signed.\");\n\n            lock (_dnssecPrivateKeys)\n            {\n                if (!_dnssecPrivateKeys.TryGetValue(keyTag, out DnssecPrivateKey privateKey))\n                    throw new DnsServerException(\"Cannot delete private key: no such private key was found.\");\n\n                if (privateKey.State != DnssecPrivateKeyState.Generated)\n                    throw new DnsServerException(\"Cannot delete private key: only keys with Generated state can be deleted.\");\n\n                _dnssecPrivateKeys.Remove(keyTag);\n            }\n        }\n\n        public void PublishAllGeneratedKeys()\n        {\n            if (_dnssecStatus == AuthZoneDnssecStatus.Unsigned)\n                throw new DnsServerException(\"The zone must be signed.\");\n\n            List<DnssecPrivateKey> generatedPrivateKeys = new List<DnssecPrivateKey>();\n            List<DnsResourceRecord> newDnsKeyRecords = new List<DnsResourceRecord>();\n\n            uint dnsKeyTtl = GetDnsKeyTtl();\n\n            lock (_dnssecPrivateKeys)\n            {\n                foreach (KeyValuePair<ushort, DnssecPrivateKey> privateKeyEntry in _dnssecPrivateKeys)","sourceCodeStart":1081,"sourceCodeEnd":1117,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Zones/PrimaryZone.cs#L1081-L1117","documentation":"Thrown by DeletePrivateKey when the matched key's State is not Generated. Only keys still in the Generated state (created but never published) can be safely removed; deleting a Published/Ready/Active/Retired key would corrupt the DNSKEY chain, so the library refuses it.","triggerScenarios":"Calling DeletePrivateKey on a key whose State is Published, Ready, Active, Retired, Dead, Revoked, or Removed rather than Generated.","commonSituations":"Trying to delete a key that has already been published or activated via PublishAllGeneratedKeys/ActivateKskDnsKey; housekeeping that targets all keys indiscriminately.","solutions":["Before deleting, check the key's State and only delete when State == DnssecPrivateKeyState.Generated.","For already-published keys, retire them through the rollover/retire workflow (RolloverDnsKey / RetireDnsKeyAsync) rather than deleting outright.","Surface a clear UI message explaining only Generated keys are deletable."],"exampleFix":"// before\nzone.DeletePrivateKey(keyTag);\n\n// after\nvar key = zone.DnssecPrivateKeys.FirstOrDefault(k => k.KeyTag == keyTag);\nif (key is null) return;\nif (key.State != DnssecPrivateKeyState.Generated)\n    throw new InvalidOperationException($\"Key {keyTag} is {key.State}; only Generated keys can be deleted.\");\nzone.DeletePrivateKey(keyTag);","handlingStrategy":"type-guard","validationCode":"// Only delete keys that are still in the Generated state\nvar key = zone.DnssecPrivateKeys.FirstOrDefault(k => k.KeyTag == keyTag);\nif (key is null) return;\nif (key.State != DnssecPrivateKeyState.Generated)\n    throw new InvalidOperationException($\"Key {keyTag} state is {key.State}; only Generated keys can be deleted.\");\nzone.DeletePrivateKey(keyTag);","typeGuard":"static bool IsKeyDeletable(DnssecPrivateKey key) =>\n    key.State == DnssecPrivateKeyState.Generated;","tryCatchPattern":"try\n{\n    zone.DeletePrivateKey(keyTag);\n}\ncatch (DnsServerException ex) when (ex.Message.Contains(\"only keys with Generated state\"))\n{\n    // key already advanced; retire it via the rollover workflow instead\n}","preventionTips":["Delete keys only while they are still Generated, before publishing.","For published keys, use RolloverDnsKey / RetireDnsKeyAsync to remove them safely."],"tags":["dnssec","private-key","state-precondition","validation","type-guard","csharp"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}