TechnitiumSoftware/DnsServer · error · DnsServerException

Cannot sign zone: the zone is already signed.

Error message

Cannot sign zone: the zone is already signed.

What it means

Thrown by the collection overload PrimaryZone.SignZone(...) when _dnssecStatus != AuthZoneDnssecStatus.Unsigned. A zone can only be signed from the Unsigned state; re-signing a zone that is already NSEC/NSEC3-signed must go through re-sign/key-roll paths. The guard raises DnsServerException as the very first validation.

Source

Thrown at DnsServerCore/Dns/Zones/PrimaryZone.cs:478

                if (saltLength > 0)
                {
                    salt = new byte[saltLength];
                    RandomNumberGenerator.Fill(salt);
                }
                else
                {
                    salt = [];
                }
            }

            SignZone([kskPrivateKey, zskPrivateKey], dnsKeyTtl, useNSec3, iterations, salt);
        }

        public void SignZone(IReadOnlyCollection<DnssecPrivateKey> dnssecPrivateKeys, uint dnsKeyTtl, bool useNSec3, ushort iterations = 0, byte[] salt = null)
        {
            //do validations
            if (_dnssecStatus != AuthZoneDnssecStatus.Unsigned)
                throw new DnsServerException("Cannot sign zone: the zone is already signed.");

            if (useNSec3)
            {
                if (iterations > 50)
                    throw new ArgumentOutOfRangeException(nameof(iterations), "NSEC3 iterations valid range is 0-50");

                if (salt.Length > 32)
                    throw new ArgumentOutOfRangeException(nameof(salt), "NSEC3 salt length valid range is 0-32");
            }

            bool foundKsk = false;
            bool foundZsk = false;

            foreach (DnssecPrivateKey dnssecPrivateKey in dnssecPrivateKeys)
            {
                switch (dnssecPrivateKey.KeyType)
                {
                    case DnssecPrivateKeyType.KeySigningKey:

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Check zone.DnssecStatus == Unsigned before calling SignZone.
  2. If already signed and you want different params, call UnsignZone() first, then SignZone.
  3. Serialize sign/unsign operations behind a lock to avoid concurrent double-sign.

Example fix

// before
zone.SignZone(keys, ttl, useNSec3: true);

// after
if (zone.DnssecStatus != AuthZoneDnssecStatus.Unsigned)
    zone.UnsignZone();
zone.SignZone(keys, ttl, useNSec3: true);
Defensive patterns

Strategy: try-catch

Validate before calling

if (zone.DnssecStatus != AuthZoneDnssecStatus.Unsigned)
    zone.UnsignZone();
zone.SignZone(keys, ttl, useNSec3);

Type guard

static bool CanSign(AuthZone z) => z.DnssecStatus == AuthZoneDnssecStatus.Unsigned;

Try / catch

try { zone.SignZone(keys, ttl, useNSec3); }
catch (DnsServerException ex) when (ex.Message.Contains("already signed"))
{ /* concurrent signer won: refresh status, no-op or unsign+retry per intent */ }

Prevention

When it happens

Trigger: Calling zone.SignZone(...) when the zone is already SignedWithNSEC or SignedWithNSEC3 — e.g., signing twice, or signing after a failed sign that left partial state under a race.

Common situations: Retry loop that re-invokes SignZone after a transient failure; UI 'Sign' button pressed twice; concurrent sign requests.

Related errors


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