TechnitiumSoftware/DnsServer · error · DnsServerException

Cannot set records: TTL cannot be greater than SOA EXPIRE.

Error message

Cannot set records: TTL cannot be greater than SOA EXPIRE.

What it means

Thrown by PrimarySubDomainZone.SetRecords() in the default case when records[0].OriginalTtlValue exceeds _primaryZone.GetZoneSoaExpire(). Enforces RFC 1035: a record TTL must not exceed the SOA EXPIRE so that secondary servers do not serve data beyond the zone's authoritative lifetime. The check uses the parent primary zone's SOA EXPIRE (the sub-domain shares the parent SOA). DnsServerException, catchable; same rule appears in ForwarderZone and PrimaryZone.

Source

Thrown at DnsServerCore/Dns/Zones/PrimarySubDomainZone.cs:94

            switch (type)
            {
                case DnsResourceRecordType.SOA:
                    throw new InvalidOperationException("Cannot set SOA record on sub domain.");

                case DnsResourceRecordType.DNSKEY:
                case DnsResourceRecordType.RRSIG:
                case DnsResourceRecordType.NSEC:
                case DnsResourceRecordType.NSEC3PARAM:
                case DnsResourceRecordType.NSEC3:
                    throw new InvalidOperationException("Cannot set DNSSEC records.");

                case DnsResourceRecordType.FWD:
                    throw new DnsServerException("The record type is not supported by primary zones.");

                default:
                    if (records[0].OriginalTtlValue > _primaryZone.GetZoneSoaExpire())
                        throw new DnsServerException("Cannot set records: TTL cannot be greater than SOA EXPIRE.");

                    if (!TrySetRecords(type, records, out IReadOnlyList<DnsResourceRecord> deletedRecords))
                        throw new DnsServerException("Cannot set records. Please try again.");

                    _primaryZone.CommitAndIncrementSerial(deletedRecords, records);

                    if (_primaryZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned)
                        _primaryZone.UpdateDnssecRecordsFor(this, type);

                    _primaryZone.TriggerNotify();
                    break;
            }
        }

        public override bool AddRecord(DnsResourceRecord record)
        {
            if (_primaryZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned)
            {

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Cap the record TTL to <= _primaryZone.GetZoneSoaExpire() before calling SetRecords.
  2. Raise the parent primary zone's SOA EXPIRE first if a longer TTL is required (via primaryZone.SetRecords(SOA,...)).
  3. Clamp TTLs at the import boundary using the parent zone's current SOA EXPIRE.

Example fix

// before
subZone.SetRecords(type, records);

// after
uint maxTtl = _primaryZone.GetZoneSoaExpire();
var clamped = records.Select(r => r.OriginalTtlValue > maxTtl
    ? new DnsResourceRecord(r.Name, r.Type, r.Class, maxTtl, r.RDATA) : r).ToList();
subZone.SetRecords(type, clamped);
Defensive patterns

Strategy: validation

Validate before calling

uint maxTtl = _primaryZone.GetZoneSoaExpire();
if (records[0].OriginalTtlValue > maxTtl)
    records = records.Select(r => new DnsResourceRecord(r.Name, r.Type, r.Class, maxTtl, r.RDATA)).ToList();
zone.SetRecords(type, records);

Type guard

static bool IsTtlWithinSoaExpire(IReadOnlyList<DnsResourceRecord> records, uint soaExpire) => records.Count > 0 && records[0].OriginalTtlValue <= soaExpire;

Try / catch

try { zone.SetRecords(type, records); }
catch (DnsServerException ex) when (ex.Message.Contains("SOA EXPIRE")) { /* clamp TTL to parent SOA EXPIRE and retry */ }

Prevention

When it happens

Trigger: subZone.SetRecords(type, records) where the first record's OriginalTtlValue > the parent primary zone's SOA EXPIRE. Triggered by importing high-TTL records or by lowering the parent SOA EXPIRE below existing record TTLs.

Common situations: Importing records with TTL 1209600+ into a zone whose SOA EXPIRE is the default; tightening SOA EXPIRE after the fact; copying records from a zone with a larger EXPIRE.

Related errors


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