{"record":{"id":"f4a43d3cd11ae0b3","repo":"TechnitiumSoftware/DnsServer","slug":"cannot-add-record-ttl-cannot-be-greater-than-soa-f4a43d","errorCode":null,"errorMessage":"Cannot add record: TTL cannot be greater than SOA EXPIRE.","messagePattern":"Cannot add record: TTL cannot be greater than SOA EXPIRE\\.","errorType":"exception","errorClass":"DnsServerException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Zones/PrimaryZone.cs","lineNumber":2658,"sourceCode":"                case DnsResourceRecordType.APP:\n                    throw new InvalidOperationException(\"Cannot add record: use SetRecords() for \" + record.Type.ToString() + \" record\");\n\n                case DnsResourceRecordType.DS:\n                    throw new InvalidOperationException(\"Cannot set DS record at zone apex.\");\n\n                case DnsResourceRecordType.DNSKEY:\n                case DnsResourceRecordType.RRSIG:\n                case DnsResourceRecordType.NSEC:\n                case DnsResourceRecordType.NSEC3PARAM:\n                case DnsResourceRecordType.NSEC3:\n                    throw new InvalidOperationException(\"Cannot add DNSSEC record.\");\n\n                case DnsResourceRecordType.FWD:\n                    throw new DnsServerException(\"The record type is not supported by primary zones.\");\n\n                default:\n                    if (record.OriginalTtlValue > GetZoneSoaExpire())\n                        throw new DnsServerException(\"Cannot add record: TTL cannot be greater than SOA EXPIRE.\");\n\n                    AddRecord(record, out IReadOnlyList<DnsResourceRecord> addedRecords, out IReadOnlyList<DnsResourceRecord> deletedRecords);\n\n                    if (addedRecords.Count > 0)\n                    {\n                        CommitAndIncrementSerial(deletedRecords, addedRecords);\n\n                        if (_dnssecStatus != AuthZoneDnssecStatus.Unsigned)\n                            UpdateDnssecRecordsFor(this, record.Type);\n\n                        TriggerNotify();\n\n                        return true;\n                    }\n\n                    return false;\n            }\n        }","sourceCodeStart":2640,"sourceCodeEnd":2676,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Zones/PrimaryZone.cs#L2640-L2676","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nzone.AddRecord(new DnsResourceRecord(name, type, klass, ttl: 604800, rdata));\n\n// after\nuint cap = zone.GetZoneSoaExpire();\nif (ttl > cap) ttl = cap;\nzone.AddRecord(new DnsResourceRecord(name, type, klass, ttl, rdata));","handlingStrategy":"validation","validationCode":"uint cap = zone.GetZoneSoaExpire();\nif (record.OriginalTtlValue > cap)\n    record = WithOriginalTtl(record, cap);\nzone.AddRecord(record);","typeGuard":"static bool TtlWithinSoaExpire(PrimaryZone zone, DnsResourceRecord r) =>\n    r.OriginalTtlValue <= zone.GetZoneSoaExpire();","tryCatchPattern":"null","preventionTips":["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."],"tags":["dns","primary-zone","ttl","soa","validation"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}