TechnitiumSoftware/DnsServer · error · DnsServerException
Cannot add record: TTL cannot be greater than SOA EXPIRE.
Error message
Cannot add record: TTL cannot be greater than SOA EXPIRE.
What it means
Thrown by PrimarySubDomainZone.AddRecord when the new record's OriginalTtlValue is greater than the parent zone's SOA EXPIRE value. DNSSEC and secondary-refresh correctness require a record TTL to fit within the SOA EXPIRE; a TTL longer than EXPIRE would let caches outlive secondary validity windows. The check uses _primaryZone.GetZoneSoaExpire() and fires a DnsServerException before the record is committed or the serial incremented.
Source
Thrown at DnsServerCore/Dns/Zones/PrimarySubDomainZone.cs:141
break;
}
}
switch (record.Type)
{
case DnsResourceRecordType.DNSKEY:
case DnsResourceRecordType.RRSIG:
case DnsResourceRecordType.NSEC:
case DnsResourceRecordType.NSEC3PARAM:
case DnsResourceRecordType.NSEC3:
throw new InvalidOperationException("Cannot add DNSSEC record.");
case DnsResourceRecordType.FWD:
throw new DnsServerException("The record type is not supported by primary zones.");
default:
if (record.OriginalTtlValue > _primaryZone.GetZoneSoaExpire())
throw new DnsServerException("Cannot add record: TTL cannot be greater than SOA EXPIRE.");
AddRecord(record, out IReadOnlyList<DnsResourceRecord> addedRecords, out IReadOnlyList<DnsResourceRecord> deletedRecords);
if (addedRecords.Count > 0)
{
_primaryZone.CommitAndIncrementSerial(deletedRecords, addedRecords);
if (_primaryZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned)
_primaryZone.UpdateDnssecRecordsFor(this, record.Type);
_primaryZone.TriggerNotify();
return true;
}
return false;
}
}View on GitHub (pinned to d0484b6c1e)
Solutions
- Lower the record TTL to be <= the zone SOA EXPIRE before adding.
- Raise the zone SOA EXPIRE (via SetRecords on the SOA RRset) to accommodate the desired TTL, then add the record.
- If importing many records, clamp/normalize their TTLs to the zone EXPIRE first.
Example fix
// before
zone.AddRecord(record); // record.OriginalTtlValue = 86400, SOA EXPIRE = 3600
// after
uint expire = zone.GetZoneSoaExpire();
if (record.OriginalTtlValue > expire)
record = record.WithTtl(Math.Min(record.OriginalTtlValue, expire));
zone.AddRecord(record); Defensive patterns
Strategy: validation
Validate before calling
uint expire = primaryZone.GetZoneSoaExpire();
if (record.OriginalTtlValue > expire)
record = record.WithTtl(expire); // or reject Type guard
static bool TtlFitsSoaExpire(DnsResourceRecord r, uint soaExpire) => r.OriginalTtlValue <= soaExpire;
Prevention
- Read the zone SOA EXPIRE once and clamp all record TTLs against it before add/update.
- Keep SOA EXPIRE >= your largest acceptable record TTL.
- Validate TTLs in import pipelines against the destination zone's SOA.
When it happens
Trigger: zone.AddRecord(record) where record.OriginalTtlValue > _primaryZone.GetZoneSoaExpire(). Typical when importing a record with a long TTL (e.g., 86400+) into a zone whose SOA EXPIRE is set low.
Common situations: Lowered SOA EXPIRE during testing; bulk import of records exported from a zone with a much larger EXPIRE; misconfigured SOA default template.
Related errors
- Cannot update record: TTL cannot be greater than SOA EXPIRE.
- Cannot update record: use SetRecords() for {oldRecord.Type}
- Zone does not contain SOA record.
- Cannot delete DNSSEC records.
- Cannot update DNSSEC records.
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/604e6f179d8b6186.
Report an issue: GitHub.