TechnitiumSoftware/DnsServer · error · InvalidOperationException

Cannot add DNSSEC record.

Error message

Cannot add DNSSEC record.

What it means

Thrown by PrimarySubDomainZone.AddRecord() when record.Type is DNSKEY, RRSIG, NSEC, NSEC3PARAM, or NSEC3. DNSSEC records are managed exclusively by the signing engine (_primaryZone.UpdateDnssecRecordsFor); manually adding any of them would corrupt the signature chain and key/rollover state. This is the AddRecord-side counterpart of error 493 and uses InvalidOperationException (API misuse) rather than DnsServerException. Note AddRecord here does not also reject SOA because a sub-domain add of SOA is structurally impossible (SOA is apex-only), but it does reject FWD at line 137.

Source

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

                    case DnsResourceRecordType.APP:
                        throw new DnsServerException("The record type is not supported by DNSSEC signed primary zones.");

                    default:
                        if (record.GetAuthGenericRecordInfo().Disabled)
                            throw new DnsServerException("Cannot add record: disabling records in a signed zones is not supported.");

                        break;
                }
            }

            switch (record.Type)
            {
                case DnsResourceRecordType.DNSKEY:
                case DnsResourceRecordType.RRSIG:
                case DnsResourceRecordType.NSEC:
                case DnsResourceRecordType.NSEC3PARAM:
                case DnsResourceRecordType.NSEC3:
                    throw new InvalidOperationException("Cannot add DNSSEC record.");

                case DnsResourceRecordType.FWD:
                    throw new DnsServerException("The record type is not supported by primary zones.");

                default:
                    if (record.OriginalTtlValue > _primaryZone.GetZoneSoaExpire())
                        throw new DnsServerException("Cannot add record: TTL cannot be greater than SOA EXPIRE.");

                    AddRecord(record, out IReadOnlyList<DnsResourceRecord> addedRecords, out IReadOnlyList<DnsResourceRecord> deletedRecords);

                    if (addedRecords.Count > 0)
                    {
                        _primaryZone.CommitAndIncrementSerial(deletedRecords, addedRecords);

                        if (_primaryZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned)
                            _primaryZone.UpdateDnssecRecordsFor(this, record.Type);

                        _primaryZone.TriggerNotify();

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Never AddRecord a DNSSEC type; let the signing engine generate DNSKEY/RRSIG/NSEC/NSEC3.
  2. Strip DNSSEC record types from imports before adding records one by one.
  3. If re-signing is the goal, trigger it through the DNSSEC API, not by adding records.

Example fix

// before
foreach (var rec in importedRecords)
    zone.AddRecord(rec);

// after
var dnssecTypes = new HashSet<DnsResourceRecordType> {
    DnsResourceRecordType.DNSKEY, DnsResourceRecordType.RRSIG,
    DnsResourceRecordType.NSEC, DnsResourceRecordType.NSEC3PARAM, DnsResourceRecordType.NSEC3 };
foreach (var rec in importedRecords)
    if (!dnssecTypes.Contains(rec.Type))
        zone.AddRecord(rec);
Defensive patterns

Strategy: validation

Validate before calling

var dnssecTypes = new HashSet<DnsResourceRecordType> { DnsResourceRecordType.DNSKEY, DnsResourceRecordType.RRSIG, DnsResourceRecordType.NSEC, DnsResourceRecordType.NSEC3PARAM, DnsResourceRecordType.NSEC3 };
if (dnssecTypes.Contains(record.Type)) throw new InvalidOperationException("DNSSEC records are managed by the signing engine.");
zone.AddRecord(record);

Type guard

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

Try / catch

try { zone.AddRecord(record); }
catch (InvalidOperationException) when (IsDnssecRecordType(record.Type)) { /* skip; signing engine owns these */ }

Prevention

When it happens

Trigger: zone.AddRecord(record) where record.Type is one of the DNSSEC types — e.g. replaying an AXFR that includes RRSIG/NSEC, or a client attempting to inject a DNSKEY.

Common situations: Signed-zone import that does not strip DNSSEC records; manual key/DS publishing attempts; test data containing RRSIG records.

Related errors


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