TechnitiumSoftware/DnsServer · error · InvalidOperationException

Cannot update DNSSEC records.

Error message

Cannot update DNSSEC records.

What it means

Thrown by PrimarySubDomainZone.UpdateRecord when the record being updated is a DNSSEC type (DNSKEY/RRSIG/NSEC/NSEC3PARAM/NSEC3). DNSSEC RRs are derived from signing keys and cannot be updated through the generic record API without desynchronizing signatures. The guard raises InvalidOperationException early in the type switch.

Source

Thrown at DnsServerCore/Dns/Zones/PrimarySubDomainZone.cs:229

                    }

                    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.");

                case DnsResourceRecordType.DNSKEY:
                case DnsResourceRecordType.RRSIG:
                case DnsResourceRecordType.NSEC:
                case DnsResourceRecordType.NSEC3PARAM:
                case DnsResourceRecordType.NSEC3:
                    throw new InvalidOperationException("Cannot update DNSSEC records.");

                default:
                    if (oldRecord.Type != newRecord.Type)
                        throw new InvalidOperationException("Old and new record types do not match.");

                    if ((_primaryZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned) && newRecord.GetAuthGenericRecordInfo().Disabled)
                        throw new DnsServerException("Cannot update record: disabling records in a signed zones is not supported.");

                    if (newRecord.OriginalTtlValue > _primaryZone.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 InvalidOperationException("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);

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Skip DNSSEC types in the update path; manage them via signing/key APIs.
  2. Unsign and re-sign if the DNSSEC data is genuinely stale.
  3. Re-export the zone without DNSSEC records before replaying edits.

Example fix

// before
zone.UpdateRecord(oldRrsig, newRrsig);

// after
if (!IsDnssecRecordType(oldRecord.Type))
    zone.UpdateRecord(oldRecord, newRecord);
Defensive patterns

Strategy: validation

Validate before calling

if (IsDnssecRecordType(oldRecord.Type)) return; // signer-managed
zone.UpdateRecord(oldRecord, newRecord);

Type guard

static bool IsDnssecRecordType(DnsResourceRecordType t) =>
    t == DnsResourceRecordType.DNSKEY || t == DnsResourceRecordType.RRSIG ||
    t == DnsResourceRecordType.NSEC || t == DnsResourceRecordType.NSEC3PARAM ||
    t == DnsResourceRecordType.NSEC3;

Prevention

When it happens

Trigger: zone.UpdateRecord(oldRecord, newRecord) with oldRecord.Type one of DNSKEY, RRSIG, NSEC, NSEC3PARAM, NSEC3.

Common situations: Re-importing a zone export that includes RRSIG/DNSKEY lines into the generic updater; tooling unaware that signed zones own these RRs.

Related errors


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