TechnitiumSoftware/DnsServer · error · InvalidOperationException

Zone Signing Key (ZSK) automatic rollover cannot be set due

Error message

Zone Signing Key (ZSK) automatic rollover cannot be set due to invalid key state.

What it means

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.

Source

Thrown at DnsServerCore/Dns/Dnssec/DnssecPrivateKey.cs:452

            {
                if (_keyType == DnssecPrivateKeyType.ZoneSigningKey)
                {
                    if (value > 365)
                        throw new ArgumentOutOfRangeException(nameof(RolloverDays), "Zone Signing Key (ZSK) automatic rollover days valid range is 0-365.");

                    switch (_state)
                    {
                        case DnssecPrivateKeyState.Generated:
                        case DnssecPrivateKeyState.Published:
                        case DnssecPrivateKeyState.Ready:
                        case DnssecPrivateKeyState.Active:
                            if (_isRetiring)
                                throw new InvalidOperationException("Zone Signing Key (ZSK) automatic rollover cannot be set since it is set to retire.");

                            break;

                        default:
                            throw new InvalidOperationException("Zone Signing Key (ZSK) automatic rollover cannot be set due to invalid key state.");
                    }
                }
                else
                {
                    if (value != 0)
                        throw new NotSupportedException("Automatic rollover is not supported for Key Signing Keys (KSK).");
                }

                _rolloverDays = value;
            }
        }

        public DnsDNSKEYRecordData DnsKey
        { get { return _dnsKey; } }

        public ushort KeyTag
        { get { return _dnsKey.ComputedKeyTag; } }

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Check key.State is one of Generated/Published/Ready/Active before assigning RolloverDays; if not, the key is terminal and rollover does not apply.
  2. Generate a fresh ZSK and configure RolloverDays on it instead of the terminal key.
  3. In config-restore code, only persist RolloverDays for keys whose state is in the active set.

Example fix

// before
foreach (var k in zone.DnssecPrivateKeys) k.RolloverDays = 30;

// after
var activeStates = new[] { DnssecPrivateKeyState.Generated, DnssecPrivateKeyState.Published, DnssecPrivateKeyState.Ready, DnssecPrivateKeyState.Active };
foreach (var k in zone.DnssecPrivateKeys)
    if (activeStates.Contains(k.State) && !k.IsRetiring)
        k.RolloverDays = 30;
Defensive patterns

Strategy: validation

Validate before calling

static readonly HashSet<DnssecPrivateKeyState> RolloverAllowedStates = new()
{
    DnssecPrivateKeyState.Generated, DnssecPrivateKeyState.Published,
    DnssecPrivateKeyState.Ready, DnssecPrivateKeyState.Active
};

if (RolloverAllowedStates.Contains(key.State) && !key.IsRetiring)
    key.RolloverDays = (ushort)days;

Type guard

static bool IsKeyInRolloverCapableState(DnssecPrivateKey k) =>
    k.KeyType == DnssecPrivateKeyType.ZoneSigningKey &&
    !k.IsRetiring &&
    (k.State == DnssecPrivateKeyState.Generated ||
     k.State == DnssecPrivateKeyState.Published ||
     k.State == DnssecPrivateKeyState.Ready ||
     k.State == DnssecPrivateKeyState.Active);

Try / catch

try { key.RolloverDays = (ushort)days; }
catch (InvalidOperationException ex) when (ex.Message.Contains("invalid key state"))
{ /* key is terminal; create a new ZSK instead */ }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13). Data as JSON: /api/errors/bc156e0a4e36ba3c. Report an issue: GitHub.