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
- Convert the target zone to a primary (or forwarder) zone, or import into an existing primary/forwarder zone.
- Check the zone's Type property before calling ImportRecords and skip or convert non-primary/forwarder zones.
- 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
- Check the zone's runtime type before calling ImportRecords.
- Keep an inventory mapping zone name -> type so imports target the right type.
- When restoring backups, recreate zones as the same type they were exported from.
- Automate imports with a type guard rather than assuming primary.
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
- The domain name '<domain>' does not belong to the zone: <zon
- Network ACL cannot have more than 255 entries.
- Cannot delete DNSSEC records.
- Cannot update record: use SetRecords() for {oldRecord.Type}
- Cannot update DNSSEC records.
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/6ce1144a38529648.
Report an issue: GitHub.