{"record":{"id":"bc156e0a4e36ba3c","repo":"TechnitiumSoftware/DnsServer","slug":"zone-signing-key-zsk-automatic-rollover-cannot-b-bc156e","errorCode":null,"errorMessage":"Zone Signing Key (ZSK) automatic rollover cannot be set due to invalid key state.","messagePattern":"Zone Signing Key \\(ZSK\\) automatic rollover cannot be set due to invalid key state\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Dnssec/DnssecPrivateKey.cs","lineNumber":452,"sourceCode":"            {\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\n        { get { return _dnsKey; } }\n\n        public ushort KeyTag\n        { get { return _dnsKey.ComputedKeyTag; } }\n","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Dnssec/DnssecPrivateKey.cs#L434-L470","documentation":"Thrown by RolloverDays setter on a ZSK whose State is not one of the four allowed rollover-start states (Generated, Published, Ready, Active). The switch's default branch catches every other DnssecPrivateKeyState value (e.g. Retired, Removed, Deleted) because auto-rollover cannot be initialized from a terminal or transitional state. It is distinct from error 342, which fires only when the state is valid but IsRetiring is true.","triggerScenarios":"Assigning RolloverDays on a ZSK whose _state has advanced past Active (Retired/Removed/Deleted) or is otherwise outside the four enumerated cases. Reachable when a config restore or API call touches a key that has finished or aborted its lifecycle.","commonSituations":"Restoring a DNSSEC backup over a key that was already retired; an operator editing a dead key's properties; a script iterating all keys and blanket-setting RolloverDays.","solutions":["Check key.State is one of Generated/Published/Ready/Active before assigning RolloverDays; if not, the key is terminal and rollover does not apply.","Generate a fresh ZSK and configure RolloverDays on it instead of the terminal key.","In config-restore code, only persist RolloverDays for keys whose state is in the active set."],"exampleFix":"// before\nforeach (var k in zone.DnssecPrivateKeys) k.RolloverDays = 30;\n\n// after\nvar activeStates = new[] { DnssecPrivateKeyState.Generated, DnssecPrivateKeyState.Published, DnssecPrivateKeyState.Ready, DnssecPrivateKeyState.Active };\nforeach (var k in zone.DnssecPrivateKeys)\n    if (activeStates.Contains(k.State) && !k.IsRetiring)\n        k.RolloverDays = 30;","handlingStrategy":"validation","validationCode":"static readonly HashSet<DnssecPrivateKeyState> RolloverAllowedStates = new()\n{\n    DnssecPrivateKeyState.Generated, DnssecPrivateKeyState.Published,\n    DnssecPrivateKeyState.Ready, DnssecPrivateKeyState.Active\n};\n\nif (RolloverAllowedStates.Contains(key.State) && !key.IsRetiring)\n    key.RolloverDays = (ushort)days;","typeGuard":"static bool IsKeyInRolloverCapableState(DnssecPrivateKey k) =>\n    k.KeyType == DnssecPrivateKeyType.ZoneSigningKey &&\n    !k.IsRetiring &&\n    (k.State == DnssecPrivateKeyState.Generated ||\n     k.State == DnssecPrivateKeyState.Published ||\n     k.State == DnssecPrivateKeyState.Ready ||\n     k.State == DnssecPrivateKeyState.Active);","tryCatchPattern":"try { key.RolloverDays = (ushort)days; }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"invalid key state\"))\n{ /* key is terminal; create a new ZSK instead */ }","preventionTips":["Only configure rollover on keys in the four active lifecycle states.","Treat terminal-state keys as read-only for rollover configuration."],"tags":["dnssec","zsk","rollover","lifecycle","state-machine"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}