{"record":{"id":"d9fb6da099a8253e","repo":"TechnitiumSoftware/DnsServer","slug":"zone-signing-key-zsk-automatic-rollover-cannot-b","errorCode":null,"errorMessage":"Zone Signing Key (ZSK) automatic rollover cannot be set since it is set to retire.","messagePattern":"Zone Signing Key \\(ZSK\\) automatic rollover cannot be set since it is set to retire\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Dnssec/DnssecPrivateKey.cs","lineNumber":447,"sourceCode":"\n        public ushort RolloverDays\n        {\n            get { return _rolloverDays; }\n            set\n            {\n                if (_keyType == DnssecPrivateKeyType.ZoneSigningKey)\n                {\n                    if (value > 365)\n                        throw new ArgumentOutOfRangeException(nameof(RolloverDays), \"Zone Signing Key (ZSK) automatic rollover days valid range is 0-365.\");\n\n                    switch (_state)\n                    {\n                        case DnssecPrivateKeyState.Generated:\n                        case DnssecPrivateKeyState.Published:\n                        case DnssecPrivateKeyState.Ready:\n                        case DnssecPrivateKeyState.Active:\n                            if (_isRetiring)\n                                throw new InvalidOperationException(\"Zone Signing Key (ZSK) automatic rollover cannot be set since it is set to retire.\");\n\n                            break;\n\n                        default:\n                            throw new InvalidOperationException(\"Zone Signing Key (ZSK) automatic rollover cannot be set due to invalid key state.\");\n                    }\n                }\n                else\n                {\n                    if (value != 0)\n                        throw new NotSupportedException(\"Automatic rollover is not supported for Key Signing Keys (KSK).\");\n                }\n\n                _rolloverDays = value;\n            }\n        }\n\n        public DnsDNSKEYRecordData DnsKey","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Dnssec/DnssecPrivateKey.cs#L429-L465","documentation":"Thrown by RolloverDays setter on a ZSK that is in a valid rollover state (Generated/Published/Ready/Active) but has _isRetiring == true. The key has already been commanded to retire, so starting a new automatic rollover cycle on it is contradictory. The setter reaches this branch only after the value passes the >365 check and the state falls into one of the four active cases.","triggerScenarios":"Calling key.RolloverDays = N (N <= 365) on a ZSK whose State is Generated/Published/Ready/Active AND whose IsRetiring is already true. Typically happens when an operator re-enables auto-rollover on a key already scheduled for removal, or when a config restore reapplies RolloverDays to a retiring key.","commonSituations":"Mid-rollover the operator changes their mind and toggles auto-rollover back on without first cancelling retirement; a bulk DNSSEC config import sets RolloverDays on every key including ones flagged retiring.","solutions":["If you want this key to keep serving with auto-rollover, first clear the retire flag (use the DNSSEC key lifecycle API to revoke retirement) so IsRetiring is false, then set RolloverDays.","If retirement is intentional, leave RolloverDays unset / set it on a replacement key instead.","Guard your config-restore logic to skip RolloverDays assignment for keys where IsRetiring is true."],"exampleFix":"// before\nkey.RolloverDays = 30; // key.IsRetiring == true\n\n// after\nif (!key.IsRetiring)\n    key.RolloverDays = 30;\nelse\n    _log.Warn($\"Skipped rollover days on retiring key {key.KeyTag}\");","handlingStrategy":"validation","validationCode":"if (key.KeyType == DnssecPrivateKeyType.ZoneSigningKey && days <= 365)\n{\n    if (key.IsRetiring)\n        throw new InvalidOperationException(\"Cannot set rollover on a retiring key.\");\n    key.RolloverDays = (ushort)days;\n}","typeGuard":"static bool CanSetRollover(DnssecPrivateKey k) =>\n    k.KeyType == DnssecPrivateKeyType.ZoneSigningKey && !k.IsRetiring;","tryCatchPattern":"try { key.RolloverDays = (ushort)days; }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"set to retire\"))\n{ /* cancel retirement first, or pick another key */ }","preventionTips":["Check IsRetiring before re-enabling auto-rollover on a key.","In config-restore code, skip RolloverDays for keys flagged retiring."],"tags":["dnssec","zsk","rollover","lifecycle","state-machine"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}