TechnitiumSoftware/DnsServer · error · InvalidOperationException
Cannot update record: use SetRecords() for {0} record
Error message
Cannot update record: use SetRecords() for {0} record What it means
Thrown by ForwarderZone.UpdateRecord() when oldRecord.Type is SOA. SOA records are special single-valued apex records whose semantics (serial, refresh, retry, expire, minimum, name server, responsible person) cannot be patched via the generic add/delete UpdateRecord flow; they must be atomically replaced via SetRecords(SOA, ...), which validates TTL<=Expire, Retry<=Refresh, Refresh<=Expire, and rewrites the responsible person to 'invalid'. The message echoes the offending type so the caller knows exactly which record type to redirect.
Source
Thrown at DnsServerCore/Dns/Zones/ForwarderZone.cs:243
if (TryDeleteRecord(type, rdata, out DnsResourceRecord deletedRecord))
{
CommitAndIncrementSerial([deletedRecord]);
TriggerNotify();
return true;
}
return false;
}
}
public override void UpdateRecord(DnsResourceRecord oldRecord, DnsResourceRecord newRecord)
{
switch (oldRecord.Type)
{
case DnsResourceRecordType.SOA:
throw new InvalidOperationException("Cannot update record: use SetRecords() for " + oldRecord.Type.ToString() + " record");
default:
if (oldRecord.Type != newRecord.Type)
throw new InvalidOperationException("Old and new record types do not match.");
if (newRecord.OriginalTtlValue > GetZoneSoaExpire())
throw new DnsServerException("Cannot update record: TTL cannot be greater than SOA EXPIRE.");
if (!TryDeleteRecord(oldRecord.Type, oldRecord.RDATA, out DnsResourceRecord deletedRecord))
throw new DnsServerException("Cannot update record: the record does not exists to be updated.");
AddRecord(newRecord, out IReadOnlyList<DnsResourceRecord> addedRecords, out IReadOnlyList<DnsResourceRecord> deletedRecords);
List<DnsResourceRecord> allDeletedRecords = new List<DnsResourceRecord>(deletedRecords.Count + 1);
allDeletedRecords.Add(deletedRecord);
allDeletedRecords.AddRange(deletedRecords);
CommitAndIncrementSerial(allDeletedRecords, addedRecords);View on GitHub (pinned to d0484b6c1e)
Solutions
- Route SOA edits through SetRecords(DnsResourceRecordType.SOA, new[] { newSoaRecord }) instead of UpdateRecord.
- Branch in your API/UI layer: if (oldRecord.Type == DnsResourceRecordType.SOA) zone.SetRecords(oldRecord.Type, new[] { newRecord }); else zone.UpdateRecord(oldRecord, newRecord);
- Ensure the new SOA passes SetRecords validation (TTL <= Expire, Retry <= Refresh, Refresh <= Expire).
Example fix
// before
zone.UpdateRecord(oldRecord, newRecord);
// after
if (oldRecord.Type == DnsResourceRecordType.SOA)
zone.SetRecords(DnsResourceRecordType.SOA, new[] { newRecord });
else
zone.UpdateRecord(oldRecord, newRecord); Defensive patterns
Strategy: validation
Validate before calling
if (oldRecord.Type == DnsResourceRecordType.SOA)
zone.SetRecords(DnsResourceRecordType.SOA, new[] { newRecord });
else
zone.UpdateRecord(oldRecord, newRecord); Type guard
static bool RequiresSetRecordsInsteadOfUpdate(DnsResourceRecordType type) => type == DnsResourceRecordType.SOA;
Try / catch
try { zone.UpdateRecord(oldRecord, newRecord); }
catch (InvalidOperationException ex) when (oldRecord.Type == DnsResourceRecordType.SOA) { zone.SetRecords(DnsResourceRecordType.SOA, new[] { newRecord }); } Prevention
- In your record-edit API, branch SOA to SetRecords before calling UpdateRecord.
- Disable the record-type field in the edit UI once a record is selected for update.
- Document that SOA, like DNSSEC records, is set-only, not updateable.
When it happens
Trigger: Calling zone.UpdateRecord(oldSoaRecord, newSoaRecord) where oldRecord.Type == DnsResourceRecordType.SOA on a ForwarderZone. Happens when generic record-edit UI/API code passes an SOA row into the same update handler used for A/AAAA/CNAME records.
Common situations: A generic 'edit record' API endpoint that does not branch on record type; an import/sync routine that round-trips SOA through the update path; clients editing SOA timers (refresh/retry/expire) via the wrong endpoint.
Related errors
- Old and new record types do not match.
- Cannot set SOA record on sub domain.
- Cannot set SOA record on sub domain.
- Cannot update record: TTL cannot be greater than SOA EXPIRE.
- Cannot update record: the record does not exists to be updat
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/a7fa9d5f6b5535a9.
Report an issue: GitHub.