TechnitiumSoftware/DnsServer · error · InvalidOperationException
Invalid SOA record.
Error message
Invalid SOA record.
What it means
Thrown as InvalidOperationException by PrimaryZone.SetRecords() in the SOA case when the input does not contain exactly one record, or that record's name does not equal the zone's apex name (_name). A zone must have exactly one SOA at its apex; any other shape is malformed input.
Source
Thrown at DnsServerCore/Dns/Zones/PrimaryZone.cs:2537
foreach (DnsResourceRecord record in records)
{
if (record.GetAuthGenericRecordInfo().Disabled)
throw new DnsServerException("Cannot set records: disabling records in a signed zones is not supported.");
}
break;
}
}
switch (type)
{
case DnsResourceRecordType.CNAME:
case DnsResourceRecordType.DS:
throw new InvalidOperationException("Cannot set " + type.ToString() + " record at zone apex.");
case DnsResourceRecordType.SOA:
if ((records.Count != 1) || !records[0].Name.Equals(_name, StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("Invalid SOA record.");
DnsResourceRecord newSoaRecord = records[0];
DnsSOARecordData newSoa = newSoaRecord.RDATA as DnsSOARecordData;
if (newSoaRecord.OriginalTtlValue > newSoa.Expire)
throw new DnsServerException("Cannot set record: TTL cannot be greater than SOA EXPIRE.");
if (newSoa.Retry > newSoa.Refresh)
throw new DnsServerException("Cannot set record: SOA RETRY cannot be greater than SOA REFRESH.");
if (newSoa.Refresh > newSoa.Expire)
throw new DnsServerException("Cannot set record: SOA REFRESH cannot be greater than SOA EXPIRE.");
//remove any record info except serial date scheme and comments
bool useSoaSerialDateScheme;
string comments;
{
SOARecordInfo recordInfo = newSoaRecord.GetAuthSOARecordInfo();View on GitHub (pinned to d0484b6c1e)
Solutions
- Ensure exactly one SOA record is passed and that its Name equals the zone apex name (case-insensitive).
- Normalize imported SOA records by overwriting their Name with the zone apex before calling SetRecords.
- Validate records.Count == 1 and the name match before the call.
Example fix
// before
zone.SetRecords(DnsResourceRecordType.SOA, soaRecords); // throws if count!=1 or name mismatch
// after
var soa = soaRecords.Single();
soa = new DnsResourceRecord(zoneName, soa.Type, soa.Class, soa.OriginalTtlValue, soa.RDATA);
zone.SetRecords(DnsResourceRecordType.SOA, new[] { soa }); Defensive patterns
Strategy: validation
Validate before calling
// Validate SOA shape before SetRecords.
if (type == DnsResourceRecordType.SOA)
{
if (records.Count != 1)
throw new ArgumentException("SOA must have exactly one record.");
if (!records[0].Name.Equals(zone.Name, StringComparison.OrdinalIgnoreCase))
throw new ArgumentException("SOA owner name must equal the zone apex.");
}
zone.SetRecords(type, records); Type guard
static bool IsValidApexSoa(IReadOnlyList<DnsResourceRecord> rs, string zoneName) =>
rs.Count == 1 && rs[0].Name.Equals(zoneName, StringComparison.OrdinalIgnoreCase); Try / catch
try { zone.SetRecords(type, records); }
catch (InvalidOperationException ex) when (ex.Message == "Invalid SOA record.")
{ Log.Error("SOA must be a single record whose name equals the zone apex."); } Prevention
- Always set the SOA owner name to the zone apex when constructing it.
- Reject SOA lists with Count != 1 upstream.
- Normalize imported SOA owner names to the target zone name.
When it happens
Trigger: Calling SetRecords(DnsResourceRecordType.SOA, records) where records.Count != 1, or where records[0].Name does not case-insensitively equal the zone apex name.
Common situations: Passing an empty or multi-element SOA list; passing an SOA record whose owner name was rewritten (e.g. to a subdomain) during import; constructing SOA records programmatically without setting the owner name to the zone name.
Related errors
- Cannot set {type} record at zone apex.
- Cannot set DS record at zone apex.
- Cannot set record: TTL cannot be greater than SOA EXPIRE.
- Cannot set record: SOA RETRY cannot be greater than SOA REFR
- Cannot set record: SOA REFRESH cannot be greater than SOA EX
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/95633d8d52ead6b8.
Report an issue: GitHub.