{"record":{"id":"c092029b514a50de","repo":"TechnitiumSoftware/DnsServer","slug":"cannot-update-record-ttl-cannot-be-greater-than-s-c09202","errorCode":null,"errorMessage":"Cannot update record: TTL cannot be greater than SOA EXPIRE.","messagePattern":"Cannot update record: TTL cannot be greater than SOA EXPIRE\\.","errorType":"exception","errorClass":"DnsServerException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Zones/PrimaryZone.cs","lineNumber":2762,"sourceCode":"                case DnsResourceRecordType.SOA:\n                    throw new InvalidOperationException(\"Cannot update record: use SetRecords() for \" + oldRecord.Type.ToString() + \" record\");\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 update DNSSEC records.\");\n\n                default:\n                    if (oldRecord.Type != newRecord.Type)\n                        throw new InvalidOperationException(\"Old and new record types do not match.\");\n\n                    if ((_dnssecStatus != AuthZoneDnssecStatus.Unsigned) && newRecord.GetAuthGenericRecordInfo().Disabled)\n                        throw new DnsServerException(\"Cannot update record: disabling records in a signed zones is not supported.\");\n\n                    if (newRecord.OriginalTtlValue > GetZoneSoaExpire())\n                        throw new DnsServerException(\"Cannot update record: TTL cannot be greater than SOA EXPIRE.\");\n\n                    if (!TryDeleteRecord(oldRecord.Type, oldRecord.RDATA, out DnsResourceRecord deletedRecord))\n                        throw new DnsServerException(\"Cannot update record: the record does not exists to be updated.\");\n\n                    AddRecord(newRecord, out IReadOnlyList<DnsResourceRecord> addedRecords, out IReadOnlyList<DnsResourceRecord> deletedRecords);\n\n                    List<DnsResourceRecord> allDeletedRecords = new List<DnsResourceRecord>(deletedRecords.Count + 1);\n                    allDeletedRecords.Add(deletedRecord);\n                    allDeletedRecords.AddRange(deletedRecords);\n\n                    CommitAndIncrementSerial(allDeletedRecords, addedRecords);\n\n                    if (_dnssecStatus != AuthZoneDnssecStatus.Unsigned)\n                        UpdateDnssecRecordsFor(this, oldRecord.Type);\n\n                    TriggerNotify();\n                    break;\n            }","sourceCodeStart":2744,"sourceCodeEnd":2780,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Zones/PrimaryZone.cs#L2744-L2780","documentation":"Thrown by PrimaryZone.UpdateRecord (default case) when newRecord.OriginalTtlValue exceeds GetZoneSoaExpire(). Same TTL cap as AddRecord (error 580): records must not outlive the zone's SOA EXPIRE window. DnsServerException indicates invalid data rather than API misuse.","triggerScenarios":"Calling zone.UpdateRecord(oldRecord, newRecord) where newRecord.OriginalTtlValue (seconds) is greater than the zone SOA EXPIRE.","commonSituations":"Raising a TTL during an edit; migrating records with 7-day TTLs into a zone with a small EXPIRE; unit vs seconds mismatch.","solutions":["Clamp newRecord.OriginalTtlValue to <= GetZoneSoaExpire() before updating.","Increase SOA EXPIRE via SetRecords(SOA, ...) if the larger TTL is required.","Validate TTL against the cap in the same helper used by AddRecord."],"exampleFix":"// before\nzone.UpdateRecord(oldRecord, newRecord); // TTL too high\n\n// after\nuint cap = zone.GetZoneSoaExpire();\nif (newRecord.OriginalTtlValue > cap)\n    newRecord = WithTtl(newRecord, cap);\nzone.UpdateRecord(oldRecord, newRecord);","handlingStrategy":"validation","validationCode":"uint cap = zone.GetZoneSoaExpire();\nif (newRecord.OriginalTtlValue > cap)\n    newRecord = WithOriginalTtl(newRecord, cap);\nzone.UpdateRecord(oldRecord, newRecord);","typeGuard":"static bool TtlWithinSoaExpire(PrimaryZone zone, DnsResourceRecord r) =>\n    r.OriginalTtlValue <= zone.GetZoneSoaExpire();","tryCatchPattern":"null","preventionTips":["Reuse the same TTL-clamp helper for AddRecord and UpdateRecord.","Surface SOA EXPIRE in record-edit UIs so users see the ceiling."],"tags":["dns","primary-zone","ttl","soa","validation","update"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}