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
- Skip DNSSEC types in the update path; manage them via signing/key APIs.
- Unsign and re-sign if the DNSSEC data is genuinely stale.
- 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
- Exclude DNSSEC types from generic update pipelines.
- Strip RRSIG/DNSKEY/NSEC lines from zone exports before replaying edits.
- Manage DNSSEC data only via sign/unsign/key-roll APIs.
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
- Cannot delete DNSSEC records.
- Cannot update record: disabling records in a signed zones is
- Cannot update record: use SetRecords() for {oldRecord.Type}
- Old and new record types do not match.
- 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/4a960d2a63917a47.
Report an issue: GitHub.