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

  1. Set RETRY <= REFRESH on the SOA before calling SetRecords.
  2. If a longer RETRY is genuinely needed, raise REFRESH to at least that value first.
  3. 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

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


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