TechnitiumSoftware/DnsServer · error · DnsWebServiceException

Cannot update record: the old record does not exists.

Error message

Cannot update record: the old record does not exists.

What it means

Thrown by UpdateRecord in the default case branch (non-CNAME/DNAME/APP records) when oldRecord.Name differs from newRecord.Name AND authZone.DeleteRecord(oldRecord.Type, oldRecord.RDATA) returns false. This means the specific old record's RDATA could not be found for deletion during a name-move operation. Notably this throws DnsWebServiceException, not DnsServerException like the other UpdateRecord errors.

Source

Thrown at DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs:2357

                        newZone.SetRecords(newRecord.Type, new DnsResourceRecord[] { newRecord });

                        if (newZone is SubDomainZone subDomainZone1)
                            subDomainZone1.AutoUpdateState();
                    }
                    break;

                default:
                    if (oldRecord.Name.Equals(newRecord.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        authZone.UpdateRecord(oldRecord, newRecord);

                        if (authZone is SubDomainZone subDomainZone)
                            subDomainZone.AutoUpdateState();
                    }
                    else
                    {
                        if (!authZone.DeleteRecord(oldRecord.Type, oldRecord.RDATA))
                            throw new DnsWebServiceException("Cannot update record: the old record does not exists.");

                        if (authZone is SubDomainZone subDomainZone)
                        {
                            if (authZone.IsEmpty)
                                _root.TryRemove(oldRecord.Name, out SubDomainZone _); //remove empty sub zone
                            else
                                subDomainZone.AutoUpdateState();
                        }

                        AuthZone newZone = GetOrAddSubDomainZone(zoneName, newRecord.Name);

                        newZone.AddRecord(newRecord);

                        if (newZone is SubDomainZone subDomainZone1)
                            subDomainZone1.AutoUpdateState();
                    }
                    break;
            }

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Re-fetch the current record from the zone before constructing oldRecord to ensure RDATA matches exactly.
  2. Use a query to get the authoritative record, then pass that exact object as oldRecord.
  3. If concurrent edits are expected, implement optimistic locking or retry the fetch-update cycle.

Example fix

// before (oldRecord may have stale RDATA)
authZoneManager.UpdateRecord("example.com", staleOldRecord, newRecord);

// after (fetch fresh record first)
var currentRecords = authZoneManager.GetRecords("example.com", oldRecord.Name, oldRecord.Type);
if (currentRecords.Count == 0)
    throw new InvalidOperationException("Old record not found; it may have been modified or deleted.");
authZoneManager.UpdateRecord("example.com", currentRecords[0], newRecord);
Defensive patterns

Strategy: validation

Validate before calling

// fetch the current authoritative record before attempting a name-move update
var current = authZoneManager.GetRecords(zoneName, oldRecord.Name, oldRecord.Type);
if (current.Count == 0 || !current[0].RDATA.Equals(oldRecord.RDATA))
    throw new InvalidOperationException("Old record RDATA does not match current zone state.");

Type guard

static bool RecordRdataMatches(DnsResourceRecord stored, DnsResourceRecord candidate)
    => stored.Type == candidate.Type && stored.RDATA.Equals(candidate.RDATA);

Try / catch

try
{
    authZoneManager.UpdateRecord(zoneName, oldRecord, newRecord);
}
catch (DnsWebServiceException ex) when (ex.Message.Contains("the old record does not exists"))
{
    // RDATA was stale; re-fetch and retry
    var fresh = authZoneManager.GetRecords(zoneName, oldRecord.Name, oldRecord.Type);
    if (fresh.Count > 0)
        authZoneManager.UpdateRecord(zoneName, fresh[0], newRecord);
}

Prevention

When it happens

Trigger: Calling UpdateRecord where the old and new records have different names (moving a record to a new name), and the old record's exact RDATA does not match what is stored in the zone. The DeleteRecord(type, rdata) overload returns false when the RDATA doesn't match any stored record, triggering this exception.

Common situations: oldRecord was constructed from stale data and its RDATA no longer matches the zone's current record (e.g. TTL or RDATA content changed since the client fetched it); concurrent modification between fetch and update; RDATA comparison fails due to normalization differences (case in domain names within RDATA, whitespace).

Related errors


AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13). Data as JSON: /api/errors/aa9dea3b8b87998f. Report an issue: GitHub.