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

  1. Lower record.OriginalTtlValue to be <= GetZoneSoaExpire() before calling AddRecord.
  2. Increase the zone's SOA EXPIRE value via SetRecords(SOA, ...) to accommodate the desired TTL.
  3. 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

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


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