TechnitiumSoftware/DnsServer · error · InvalidOperationException
Old and new record types do not match.
Error message
Old and new record types do not match.
What it means
Thrown by ForwarderZone.UpdateRecord() in the default case when oldRecord.Type != newRecord.Type. UpdateRecord implements record mutation as a delete-then-add with shared commit/serial, and allowing a type change there would corrupt the per-type _entries dictionary and the SOA serial bookkeeping. DNS updates that change a record's type are not an update; they are a delete of one type plus an add of another, so the API enforces type identity. This is a programming-contract violation (InvalidOperationException), not a transient or data condition.
Source
Thrown at DnsServerCore/Dns/Zones/ForwarderZone.cs:247
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);
TriggerNotify();
break;
}View on GitHub (pinned to d0484b6c1e)
Solutions
- Keep the record type immutable across an update: construct newRecord with the same DnsResourceRecordType as oldRecord.
- If a type change is genuinely required, perform zone.DeleteRecord(oldType, oldRdata) then zone.AddRecord(newRecord) as two operations instead of UpdateRecord.
- Validate type equality in your API layer and return a 400 before calling UpdateRecord.
Example fix
// before var newRecord = new DnsResourceRecord(name, pickedType, ...); zone.UpdateRecord(oldRecord, newRecord); // after var newRecord = new DnsResourceRecord(name, oldRecord.Type, ...); // keep type zone.UpdateRecord(oldRecord, newRecord);
Defensive patterns
Strategy: validation
Validate before calling
if (oldRecord.Type != newRecord.Type)
throw new ArgumentException("Record type cannot change on update; delete+add instead.");
zone.UpdateRecord(oldRecord, newRecord); Type guard
static bool IsTypeCompatibleUpdate(DnsResourceRecord oldRecord, DnsResourceRecord newRecord) => oldRecord.Type == newRecord.Type;
Try / catch
try { zone.UpdateRecord(oldRecord, newRecord); }
catch (InvalidOperationException ex) when (ex.Message.Contains("types do not match")) { /* perform delete(oldType) + add(newType) instead */ } Prevention
- Construct newRecord by copying oldRecord.Type, never from a user-mutable type selector on edits.
- Return HTTP 400 for type-mismatched updates at the API boundary instead of reaching the zone layer.
- Add a DTO validator: editRecordDto.Type must equal the persisted record's type.
When it happens
Trigger: Calling zone.UpdateRecord(oldRecord, newRecord) where oldRecord is e.g. an A record and newRecord is an AAAA record (or any type mismatch). Common when a client builds newRecord from form input that lets the user change the record-type dropdown, or when copying fields between record DTOs without preserving Type.
Common situations: Edit-record UI with a mutable type selector that was not disabled for edits; DTO mapping that omits Type and defaults it; refactoring that swaps record constructors.
Related errors
- Cannot update record: use SetRecords() for {0} record
- Cannot update record: the record does not exists to be updat
- Cannot set SOA record on sub domain.
- Cannot update record: TTL cannot be greater than SOA EXPIRE.
- The Query Access option is invalid for {0} zones: {1}
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/1f3b7b8f51be22fb.
Report an issue: GitHub.