{"record":{"id":"72e5a4f99420de28","repo":"TechnitiumSoftware/DnsServer","slug":"cannot-set-records-ttl-cannot-be-greater-than-soa-72e5a4","errorCode":null,"errorMessage":"Cannot set records: TTL cannot be greater than SOA EXPIRE.","messagePattern":"Cannot set records: TTL cannot be greater than SOA EXPIRE\\.","errorType":"exception","errorClass":"DnsServerException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Zones/PrimaryZone.cs","lineNumber":2605,"sourceCode":"                        }\n                    }\n\n                    TriggerNotify();\n                    break;\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 set DNSSEC records.\");\n\n                case DnsResourceRecordType.FWD:\n                    throw new DnsServerException(\"The record type is not supported by primary zones.\");\n\n                default:\n                    if (records[0].OriginalTtlValue > GetZoneSoaExpire())\n                        throw new DnsServerException(\"Cannot set records: TTL cannot be greater than SOA EXPIRE.\");\n\n                    if (!TrySetRecords(type, records, out IReadOnlyList<DnsResourceRecord> deletedRecords))\n                        throw new DnsServerException(\"Cannot set records. Please try again.\");\n\n                    CommitAndIncrementSerial(deletedRecords, records);\n\n                    if (_dnssecStatus != AuthZoneDnssecStatus.Unsigned)\n                        UpdateDnssecRecordsFor(this, type);\n\n                    TriggerNotify();\n                    break;\n            }\n        }\n\n        public override bool AddRecord(DnsResourceRecord record)\n        {\n            if (_dnssecStatus != AuthZoneDnssecStatus.Unsigned)\n            {","sourceCodeStart":2587,"sourceCodeEnd":2623,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Zones/PrimaryZone.cs#L2587-L2623","documentation":"Thrown by PrimaryZone.SetRecords() in the default record-type branch when records[0].OriginalTtlValue is greater than the zone's current SOA EXPIRE (GetZoneSoaExpire()). A TTL longer than EXPIRE lets resolvers cache data beyond the window secondaries guarantee valid data, so the server caps record TTLs at the zone's SOA EXPIRE.","triggerScenarios":"Calling SetRecords for a normal record type (A, AAAA, MX, etc.) where the first record's TTL exceeds the zone's SOA EXPIRE value.","commonSituations":"Copying records with very long TTLs (e.g. 604800) into a zone with a short EXPIRE (e.g. 3600); lowering a zone's SOA EXPIRE without re-evaluating existing record TTLs; template record sets with fixed high TTLs.","solutions":["Lower the record TTL to <= the zone's SOA EXPIRE before calling SetRecords.","If a long TTL is required, raise the zone's SOA EXPIRE (via SetRecords SOA) to at least the desired TTL first.","Clamp TTLs against GetZoneSoaExpire() when building the record set."],"exampleFix":"// before\nzone.SetRecords(type, records); // throws if TTL > SOA EXPIRE\n\n// after\nuint cap = zone.GetZoneSoaExpire();\nvar clamped = records.Select(r => r.OriginalTtlValue > cap\n    ? new DnsResourceRecord(r.Name, r.Type, r.Class, cap, r.RDATA)\n    : r).ToList();\nzone.SetRecords(type, clamped);","handlingStrategy":"validation","validationCode":"// Clamp record TTL to the zone SOA EXPIRE before SetRecords.\nuint cap = zone.GetZoneSoaExpire();\nrecords = records.Select(r => r.OriginalTtlValue > cap\n    ? new DnsResourceRecord(r.Name, r.Type, r.Class, cap, r.RDATA)\n    : r).ToList();\n\nzone.SetRecords(type, records);","typeGuard":"static bool TtlWithinZoneExpire(IReadOnlyList<DnsResourceRecord> rs, uint soaExpire) =>\n    rs.Count > 0 && rs[0].OriginalTtlValue <= soaExpire;","tryCatchPattern":"try { zone.SetRecords(type, records); }\ncatch (DnsServerException ex) when (ex.Message == \"Cannot set records: TTL cannot be greater than SOA EXPIRE.\")\n{ Log.Error($\"Clamp TTL to <= {zone.GetZoneSoaExpire()}.\"); }","preventionTips":["Clamp all record TTLs against GetZoneSoaExpire() before writing.","When lowering SOA EXPIRE, re-evaluate existing record TTLs.","Centralize TTL capping in the record-building helper."],"tags":["dns","record","ttl","soa","expire","validation"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}