TechnitiumSoftware/DnsServer · error · DnsServerException

Cannot update record: the record does not exists to be updat

Error message

Cannot update record: the record does not exists to be updated.

What it means

Thrown by ForwarderZone.UpdateRecord() when TryDeleteRecord(oldRecord.Type, oldRecord.RDATA) returns false, meaning no existing record matched the old record's (type, rdata) pair. UpdateRecord is implemented as delete-old-then-add-new inside one commit; if the old record is absent there is nothing to update and the add-then-commit would silently create a record without removing one, so the operation is rejected. The message notes the record 'does not exists' (sic) to be updated. DnsServerException, so catchable by API clients.

Source

Thrown at DnsServerCore/Dns/Zones/ForwarderZone.cs:253

            }
        }

        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;
            }
        }

        public override IReadOnlyList<DnsResourceRecord> QueryRecords(DnsResourceRecordType type, bool dnssecOk)
        {
            if (this is CatalogZone)
                return base.QueryRecords(type, dnssecOk);

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Re-fetch the current record set (QueryRecords) immediately before updating and rebuild oldRecord from the live entry so its RDATA matches exactly.
  2. If the old record is genuinely gone, switch to AddRecord(newRecord) instead of UpdateRecord.
  3. Handle this as a known concurrent-edit case: return a 409 Conflict to the client and let them refresh.

Example fix

// before
zone.UpdateRecord(staleOldRecord, newRecord);

// after
var live = zone.QueryRecords(type, false)
    .FirstOrDefault(r => r.RDATA.Equals(staleOldRecord.RDATA));
if (live == null)
    zone.AddRecord(newRecord); // old gone -> create instead
else
    zone.UpdateRecord(live, newRecord);
Defensive patterns

Strategy: validation

Validate before calling

var live = zone.QueryRecords(oldRecord.Type, false).FirstOrDefault(r => r.RDATA.Equals(oldRecord.RDATA));
if (live == null) { zone.AddRecord(newRecord); return; }
zone.UpdateRecord(live, newRecord);

Type guard

static bool RecordStillExists(Zone zone, DnsResourceRecord oldRecord) => zone.QueryRecords(oldRecord.Type, false).Any(r => r.RDATA.Equals(oldRecord.RDATA));

Try / catch

try { zone.UpdateRecord(oldRecord, newRecord); }
catch (DnsServerException ex) when (ex.Message.Contains("does not exists")) { zone.AddRecord(newRecord); }

Prevention

When it happens

Trigger: Calling zone.UpdateRecord(oldRecord, newRecord) where oldRecord no longer exists in the zone (already deleted, or RDATA does not byte-match an entry), or where oldRecord was constructed with RDATA that differs from the stored record (e.g. canonicalized differently). Concurrent modification between a list-query and the update also produces this.

Common situations: Stale client view: user lists records, another process deletes one, user submits an edit on the deleted record; RDATA normalization differences (case, compression, trailing dot) causing TryDeleteRecord to miss; retrying an update after a partial failure.

Related errors


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