TechnitiumSoftware/DnsServer · error · DnsServerException
Cannot set record: SOA RETRY cannot be greater than SOA REFR
Error message
Cannot set record: SOA RETRY cannot be greater than SOA REFRESH.
What it means
Thrown by PrimaryZone.SetRecords() in the SOA case when newSoa.Retry > newSoa.Refresh. RETRY is the interval a secondary waits before retrying a failed refresh; it must not exceed REFRESH, otherwise the retry schedule is inconsistent with the refresh schedule. The server rejects this invariant.
Source
Thrown at DnsServerCore/Dns/Zones/PrimaryZone.cs:2546
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();
useSoaSerialDateScheme = recordInfo.UseSoaSerialDateScheme;
comments = recordInfo.Comments;
}
newSoaRecord.Tag = null; //remove old record info
{
SOARecordInfo recordInfo = newSoaRecord.GetAuthSOARecordInfo();View on GitHub (pinned to d0484b6c1e)
Solutions
- Set RETRY <= REFRESH on the SOA before calling SetRecords.
- If a longer RETRY is genuinely needed, raise REFRESH to at least that value first.
- Validate Retry <= Refresh when constructing the SOA.
Example fix
// before
var soa = new DnsSOARecordData { Refresh = 3600, Retry = 9000 };
zone.SetRecords(DnsResourceRecordType.SOA, new[] { BuildSoaRecord(ttl, soa) }); // throws
// after
var soa = new DnsSOARecordData { Refresh = 9000, Retry = 3600 };
zone.SetRecords(DnsResourceRecordType.SOA, new[] { BuildSoaRecord(ttl, soa) }); Defensive patterns
Strategy: validation
Validate before calling
if (type == DnsResourceRecordType.SOA)
{
var soa = (DnsSOARecordData)records[0].RDATA;
if (soa.Retry > soa.Refresh)
throw new ArgumentException("SOA RETRY must be <= SOA REFRESH.");
}
zone.SetRecords(type, records); Type guard
static bool SoaRetryWithinRefresh(DnsResourceRecord soaRecord)
{
var soa = (DnsSOARecordData)soaRecord.RDATA;
return soa.Retry <= soa.Refresh;
} Try / catch
try { zone.SetRecords(type, records); }
catch (DnsServerException ex) when (ex.Message.Contains("SOA RETRY cannot be greater than SOA REFRESH"))
{ Log.Error("Set SOA RETRY <= SOA REFRESH."); } Prevention
- Keep RETRY <= REFRESH in SOA templates.
- Double-check timer pairs when hand-editing SOA values.
- Validate SOA timers in a shared helper before any SOA write.
When it happens
Trigger: Calling SetRecords with an SOA whose Retry value is greater than its Refresh value (e.g. Refresh 3600, Retry 9000).
Common situations: Manually editing SOA timers and swapping RETRY/REFRESH; importing a zone with malformed timers; defaults from a template where RETRY was raised without raising REFRESH.
Related errors
- Cannot set record: SOA REFRESH cannot be greater than SOA EX
- Invalid SOA record.
- Cannot set record: TTL cannot be greater than SOA EXPIRE.
- Cannot set records: TTL cannot be greater than SOA EXPIRE.
- Zone does not contain SOA record.
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/3c159f697706b3b5.
Report an issue: GitHub.