TechnitiumSoftware/DnsServer · error · DnsServerException

Zone must be a primary or forwarder type: {apexZone}

Error message

Zone must be a primary or forwarder type: {apexZone}

What it means

Thrown by ImportRecords after the zone is found but it is neither a PrimaryZone nor a ForwarderZone. ImportRecords only writes records into primary or forwarder zones because those are the authoritative, writable zone types; importing into a secondary/cache/other type is rejected. The offending zone's type is included in the message.

Source

Thrown at DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs:3067

            condensedRecords.AddRange(deletedGlueRecords);

            condensedRecords.Add(lastAddedSoaRecord);
            condensedRecords.AddRange(addedRecords);
            condensedRecords.AddRange(addedGlueRecords);

            condensedRecords.Add(lastSoaRecord);

            return condensedRecords;
        }

        internal void ImportRecords(string zoneName, IReadOnlyList<DnsResourceRecord> records, bool overwriteRecords, bool overwriteZone, bool overwriteSoaSerial)
        {
            _ = _root.FindZone(zoneName, out _, out _, out ApexZone apexZone, out _);
            if ((apexZone is null) || !apexZone.Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase))
                throw new DnsServerException("No such zone was found: " + zoneName);

            if ((apexZone is not PrimaryZone) && (apexZone is not ForwarderZone))
                throw new DnsServerException("Zone must be a primary or forwarder type: " + apexZone.ToString());

            if (overwriteZone)
            {
                //remove all existing records from the zone
                DeleteAllRecords(zoneName);

                overwriteRecords = true; //set to true for optimization
            }

            List<DnsResourceRecord> soaRRSet = null;

            foreach (KeyValuePair<string, Dictionary<DnsResourceRecordType, List<DnsResourceRecord>>> zoneEntry in DnsResourceRecord.GroupRecords(records))
            {
                if (zoneName.Equals(zoneEntry.Key, StringComparison.OrdinalIgnoreCase))
                {
                    foreach (KeyValuePair<DnsResourceRecordType, List<DnsResourceRecord>> rrsetEntry in zoneEntry.Value)
                    {
                        switch (rrsetEntry.Key)

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Convert the target zone to a primary (or forwarder) zone, or import into an existing primary/forwarder zone.
  2. Check the zone's Type property before calling ImportRecords and skip or convert non-primary/forwarder zones.
  3. Re-add the zone as PrimaryZone before importing the records.

Example fix

// before
server.AuthZoneManager.ImportRecords("example.com", records, true, false, false); // zone is SecondaryZone -> throws
// after
// (re)create as primary, then import
server.AuthZoneManager.DeleteZone("example.com");
server.AuthZoneManager.AddPrimaryZone("example.com");
server.AuthZoneManager.ImportRecords("example.com", records, true, false, false);
Defensive patterns

Strategy: validation

Validate before calling

ApexZone apex = FindApex(zoneName);
if (apex is not PrimaryZone && apex is not ForwarderZone)
    throw new InvalidOperationException($"Zone '{zoneName}' is {apex?.GetType().Name}; convert to primary/forwarder before import.");

Type guard

static bool IsImportable(ApexZone zone) => zone is PrimaryZone || zone is ForwarderZone;

Try / catch

try
{
    server.AuthZoneManager.ImportRecords(zoneName, records, true, false, false);
}
catch (DnsServerException ex) when (ex.Message.Contains("primary or forwarder type"))
{
    // convert to primary then import
    RecreateAsPrimary(zoneName);
    server.AuthZoneManager.ImportRecords(zoneName, records, true, false, false);
}

Prevention

When it happens

Trigger: Calling ImportRecords against a SecondaryZone, StubZone, CacheZone, or blocked zone; trying to bulk-load records into a zone that derives its data from a master rather than local config.

Common situations: Mistakenly importing a primary-zone backup into a zone that was recreated as secondary; automation that imports into whichever zone matches a name without checking its type.

Related errors


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