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

  1. Lower the record TTL to be <= the zone SOA EXPIRE before adding.
  2. Raise the zone SOA EXPIRE (via SetRecords on the SOA RRset) to accommodate the desired TTL, then add the record.
  3. 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

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


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