TechnitiumSoftware/DnsServer · error · DnsServerException

Cannot update NSEC3 parameters: the zone must be signed with

Error message

Cannot update NSEC3 parameters: the zone must be signed with NSEC3 first.

What it means

Thrown by PrimaryZone.UpdateNSec3Parameters when the zone's DNSSEC status is not SignedWithNSEC3. Updating NSEC3 parameters only makes sense once an NSEC3 chain exists; calling it on an Unsigned zone or an NSEC-signed zone is rejected because there is no NSEC3PARAM record to rewrite.

Source

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

                throw new ArgumentOutOfRangeException(nameof(saltLength), "NSEC3 salt length valid range is 0-32");

            lock (_dnssecUpdateLock)
            {
                IReadOnlyList<AuthZone> zones = _dnsServer.AuthZoneManager.GetApexZoneWithSubDomainZones(_name);

                DisableNSec(zones);
                EnableNSec3(zones, iterations, saltLength);

                _dnssecStatus = AuthZoneDnssecStatus.SignedWithNSEC3;
            }

            TriggerNotify();
        }

        public void UpdateNSec3Parameters(ushort iterations, byte saltLength)
        {
            if (_dnssecStatus != AuthZoneDnssecStatus.SignedWithNSEC3)
                throw new DnsServerException("Cannot update NSEC3 parameters: the zone must be signed with NSEC3 first.");

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

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

            lock (_dnssecUpdateLock)
            {
                IReadOnlyList<AuthZone> zones = _dnsServer.AuthZoneManager.GetApexZoneWithSubDomainZones(_name);

                DisableNSec3(zones);

                //since zones were removed when disabling NSEC3; get updated non empty zones list
                List<AuthZone> nonEmptyZones = new List<AuthZone>(zones.Count);

                foreach (AuthZone zone in zones)
                {

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. If the zone is Unsigned, sign it first (optionally with NSEC3) via SignZone before adjusting parameters.
  2. If the zone is SignedWithNSEC, call ConvertToNSec3 to establish NSEC3, then use UpdateNSec3Parameters for future changes.
  3. Guard the call by checking zone.DnssecStatus == SignedWithNSEC3.

Example fix

// before
zone.UpdateNSec3Parameters(iterations: 1, saltLength: 8);

// after
if (zone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3)
    zone.UpdateNSec3Parameters(iterations: 1, saltLength: 8);
else if (zone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC)
    zone.ConvertToNSec3(iterations: 1, saltLength: 8);
Defensive patterns

Strategy: validation

Validate before calling

// Only update NSEC3 parameters on an NSEC3-signed zone
if (zone.DnssecStatus != AuthZoneDnssecStatus.SignedWithNSEC3)
{
    if (zone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC)
        zone.ConvertToNSec3(iterations, saltLength);
    else
        zone.SignZone(keys, dnsKeyTtl, useNSec3: true, iterations, salt);
    return;
}
zone.UpdateNSec3Parameters(iterations, saltLength);

Type guard

static bool CanUpdateNSec3Parameters(ApexZone zone) =>
    zone.DnssecStatus == AuthZoneDnssecStatus.SignedWithNSEC3;

Try / catch

try
{
    zone.UpdateNSec3Parameters(iterations, saltLength);
}
catch (DnsServerException ex) when (ex.Message.Contains("must be signed with NSEC3 first"))
{
    // convert or sign with NSEC3 first, then retry
}

Prevention

When it happens

Trigger: Calling UpdateNSec3Parameters on a zone whose DnssecStatus is Unsigned or SignedWithNSEC.

Common situations: A maintenance script re-applies the same NSEC3 settings to all signed zones without checking which variant each one uses; an NSEC-signed zone hits this path.

Related errors


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