TechnitiumSoftware/DnsServer · error · DnsServerException
Cannot update record: TTL cannot be greater than SOA EXPIRE.
Error message
Cannot update record: TTL cannot be greater than SOA EXPIRE.
What it means
Thrown by ForwarderZone.UpdateRecord() when newRecord.OriginalTtlValue exceeds GetZoneSoaExpire(). The check enforces RFC 1035 SOA semantics: a record's TTL must not outlive the zone's SOA EXPIRE, because slaves would be permitted to serve stale data past the point the zone is considered stale. The default forwarder-zone dummy SOA sets Expire to 604800 (7 days). This is a DnsServerException (a domain/operational error, surfaced as an HTTP failure to API clients), so it is catchable.
Source
Thrown at DnsServerCore/Dns/Zones/ForwarderZone.cs:250
}
return false;
}
}
public override void UpdateRecord(DnsResourceRecord oldRecord, DnsResourceRecord newRecord)
{
switch (oldRecord.Type)
{
case DnsResourceRecordType.SOA:
throw new InvalidOperationException("Cannot update record: use SetRecords() for " + oldRecord.Type.ToString() + " record");
default:
if (oldRecord.Type != newRecord.Type)
throw new InvalidOperationException("Old and new record types do not match.");
if (newRecord.OriginalTtlValue > GetZoneSoaExpire())
throw new DnsServerException("Cannot update record: TTL cannot be greater than SOA EXPIRE.");
if (!TryDeleteRecord(oldRecord.Type, oldRecord.RDATA, out DnsResourceRecord deletedRecord))
throw new DnsServerException("Cannot update record: the record does not exists to be updated.");
AddRecord(newRecord, out IReadOnlyList<DnsResourceRecord> addedRecords, out IReadOnlyList<DnsResourceRecord> deletedRecords);
List<DnsResourceRecord> allDeletedRecords = new List<DnsResourceRecord>(deletedRecords.Count + 1);
allDeletedRecords.Add(deletedRecord);
allDeletedRecords.AddRange(deletedRecords);
CommitAndIncrementSerial(allDeletedRecords, addedRecords);
TriggerNotify();
break;
}
}
public override IReadOnlyList<DnsResourceRecord> QueryRecords(DnsResourceRecordType type, bool dnssecOk)View on GitHub (pinned to d0484b6c1e)
Solutions
- Lower newRecord.OriginalTtlValue to <= GetZoneSoaExpire() (default 604800 seconds for a forwarder zone).
- If a longer TTL is required, raise the forwarder zone's SOA EXPIRE via SetRecords(SOA, ...) first.
- Clamp incoming TTLs at your import boundary: ttl = Math.Min(ttl, (int)zone.GetZoneSoaExpire()).
Example fix
// before newRecord = new DnsResourceRecord(name, type, cls, importedTtl, rdata); zone.UpdateRecord(oldRecord, newRecord); // after uint maxTtl = zone.GetZoneSoaExpire(); uint safeTtl = Math.Min(importedTtl, maxTtl); newRecord = new DnsResourceRecord(name, type, cls, safeTtl, rdata); zone.UpdateRecord(oldRecord, newRecord);
Defensive patterns
Strategy: validation
Validate before calling
uint maxTtl = zone.GetZoneSoaExpire();
if (newRecord.OriginalTtlValue > maxTtl)
newRecord = new DnsResourceRecord(newRecord.Name, newRecord.Type, newRecord.Class, maxTtl, newRecord.RDATA);
zone.UpdateRecord(oldRecord, newRecord); Type guard
static bool IsTtlWithinSoaExpire(DnsResourceRecord rec, uint soaExpire) => rec.OriginalTtlValue <= soaExpire;
Try / catch
try { zone.UpdateRecord(oldRecord, newRecord); }
catch (DnsServerException ex) when (ex.Message.Contains("SOA EXPIRE")) { /* clamp TTL and retry */ } Prevention
- Always clamp record TTLs against GetZoneSoaExpire() at the import/API boundary.
- When raising a record TTL, verify the zone's SOA EXPIRE first and raise it if needed.
- Remember the default forwarder-zone SOA EXPIRE is 604800s (7 days).
When it happens
Trigger: zone.UpdateRecord(oldRecord, newRecord) where newRecord.OriginalTtlValue (the un-capped TTL before zone-minimum clamping) > the zone's SOA EXPIRE value. Triggered by importing records with very high TTLs, or by raising the record TTL without first raising SOA EXPIRE.
Common situations: Bulk import of external zone data with TTLs like 1209600 (14d); copying records from a primary zone whose SOA EXPIRE is larger than the forwarder's 604800; misreading OriginalTtlValue vs the already-clamped display TTL.
Related errors
- Cannot set records: TTL cannot be greater than SOA EXPIRE.
- Cannot update record: the record does not exists to be updat
- Cannot set SOA record on sub domain.
- Cannot update record: use SetRecords() for {0} record
- Old and new record types do not match.
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/7f3afb181311bb8f.
Report an issue: GitHub.