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 PrimaryZone.AddRecord (default case) when the record being added has an OriginalTtlValue greater than the zone's SOA EXPIRE. The library enforces this cap so records never outlive the zone's validity window, which would break secondary refresh semantics. It is a DnsServerException, not an InvalidOperationException, so it represents an invalid-data condition rather than a misuse of the API.
Source
Thrown at DnsServerCore/Dns/Zones/PrimaryZone.cs:2658
case DnsResourceRecordType.APP:
throw new InvalidOperationException("Cannot add record: use SetRecords() for " + record.Type.ToString() + " record");
case DnsResourceRecordType.DS:
throw new InvalidOperationException("Cannot set DS record at zone apex.");
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 > 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)
{
CommitAndIncrementSerial(deletedRecords, addedRecords);
if (_dnssecStatus != AuthZoneDnssecStatus.Unsigned)
UpdateDnssecRecordsFor(this, record.Type);
TriggerNotify();
return true;
}
return false;
}
}View on GitHub (pinned to d0484b6c1e)
Solutions
- Lower record.OriginalTtlValue to be <= GetZoneSoaExpire() before calling AddRecord.
- Increase the zone's SOA EXPIRE value via SetRecords(SOA, ...) to accommodate the desired TTL.
- Normalize imported TTLs in a helper that clamps to the zone's SOA EXPIRE.
Example fix
// before zone.AddRecord(new DnsResourceRecord(name, type, klass, ttl: 604800, rdata)); // after uint cap = zone.GetZoneSoaExpire(); if (ttl > cap) ttl = cap; zone.AddRecord(new DnsResourceRecord(name, type, klass, ttl, rdata));
Defensive patterns
Strategy: validation
Validate before calling
uint cap = zone.GetZoneSoaExpire();
if (record.OriginalTtlValue > cap)
record = WithOriginalTtl(record, cap);
zone.AddRecord(record); Type guard
static bool TtlWithinSoaExpire(PrimaryZone zone, DnsResourceRecord r) =>
r.OriginalTtlValue <= zone.GetZoneSoaExpire(); Try / catch
null
Prevention
- Keep a shared ClampTtlToSoaExpire(zone, record) helper for add and update paths.
- Treat SOA EXPIRE as the authoritative TTL ceiling for all records in a zone.
- Normalize TTLs during import so legacy long TTLs never reach AddRecord.
When it happens
Trigger: Calling zone.AddRecord(record) where record.OriginalTtlValue (seconds) exceeds GetZoneSoaExpire() (the SOA record's EXPIRE field). Occurs for any non-DNSSEC, non-FWD record type that falls into the default switch case.
Common situations: Importing records from another DNS server with long TTLs (e.g. 604800s = 7 days) into a zone whose SOA EXPIRE is smaller; restoring a zone backup without normalizing TTLs; setting a TTL from minutes vs seconds confusion.
Related errors
- Cannot update record: TTL cannot be greater than SOA EXPIRE.
- Cannot add record: TTL cannot be greater than SOA EXPIRE.
- Cannot update record: TTL cannot be greater than SOA EXPIRE.
- Cannot set record: TTL cannot be greater than SOA EXPIRE.
- Cannot set records: TTL cannot be greater than SOA EXPIRE.
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/f4a43d3cd11ae0b3.
Report an issue: GitHub.