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 PrimaryZone.SetRecords() in the default record-type branch when records[0].OriginalTtlValue is greater than the zone's current SOA EXPIRE (GetZoneSoaExpire()). A TTL longer than EXPIRE lets resolvers cache data beyond the window secondaries guarantee valid data, so the server caps record TTLs at the zone's SOA EXPIRE.

Source

Thrown at DnsServerCore/Dns/Zones/PrimaryZone.cs:2605

                        }
                    }

                    TriggerNotify();
                    break;

                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 > 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.");

                    CommitAndIncrementSerial(deletedRecords, records);

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

                    TriggerNotify();
                    break;
            }
        }

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

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Lower the record TTL to <= the zone's SOA EXPIRE before calling SetRecords.
  2. If a long TTL is required, raise the zone's SOA EXPIRE (via SetRecords SOA) to at least the desired TTL first.
  3. Clamp TTLs against GetZoneSoaExpire() when building the record set.

Example fix

// before
zone.SetRecords(type, records); // throws if TTL > SOA EXPIRE

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

Strategy: validation

Validate before calling

// Clamp record TTL to the zone SOA EXPIRE before SetRecords.
uint cap = zone.GetZoneSoaExpire();
records = records.Select(r => r.OriginalTtlValue > cap
    ? new DnsResourceRecord(r.Name, r.Type, r.Class, cap, r.RDATA)
    : r).ToList();

zone.SetRecords(type, records);

Type guard

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

Try / catch

try { zone.SetRecords(type, records); }
catch (DnsServerException ex) when (ex.Message == "Cannot set records: TTL cannot be greater than SOA EXPIRE.")
{ Log.Error($"Clamp TTL to <= {zone.GetZoneSoaExpire()}."); }

Prevention

When it happens

Trigger: Calling SetRecords for a normal record type (A, AAAA, MX, etc.) where the first record's TTL exceeds the zone's SOA EXPIRE value.

Common situations: Copying records with very long TTLs (e.g. 604800) into a zone with a short EXPIRE (e.g. 3600); lowering a zone's SOA EXPIRE without re-evaluating existing record TTLs; template record sets with fixed high TTLs.

Related errors


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