TechnitiumSoftware/DnsServer · error · InvalidOperationException

Cannot set DNSSEC records.

Error message

Cannot set DNSSEC records.

What it means

Thrown by PrimarySubDomainZone.SetRecords() when type is DNSKEY, RRSIG, NSEC, NSEC3PARAM, or NSEC3. DNSSEC records are generated and maintained automatically by the signing engine (see _primaryZone.UpdateDnssecRecordsFor(...)); letting a caller write them directly would desync signatures, key rollover state, and NSEC/NSEC3 chains. This is a hard InvalidOperationException, not a domain DnsServerException, because it signals a misuse of the API rather than a data problem. The same guard exists in AddRecord/DeleteRecord for symmetry.

Source

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

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

                        break;
                }
            }

            switch (type)
            {
                case DnsResourceRecordType.SOA:
                    throw new InvalidOperationException("Cannot set SOA record on sub domain.");

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

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

                default:
                    if (records[0].OriginalTtlValue > _primaryZone.GetZoneSoaExpire())
                        throw new DnsServerException("Cannot set records: TTL cannot be greater than SOA EXPIRE.");

                    if (!TrySetRecords(type, records, out IReadOnlyList<DnsResourceRecord> deletedRecords))
                        throw new DnsServerException("Cannot set records. Please try again.");

                    _primaryZone.CommitAndIncrementSerial(deletedRecords, records);

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

                    _primaryZone.TriggerNotify();
                    break;

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Exclude DNSSEC record types (DNSKEY, RRSIG, NSEC, NSEC3PARAM, NSEC3) from any SetRecords call; let the signing engine produce them.
  2. When importing a signed zone, import only the user records (A, AAAA, MX, etc.) and re-sign on the server.
  3. If publishing a trust anchor, use the DS record at the parent, not DNSKEY at the child.

Example fix

// before
foreach (var grp in importedRecords.GroupBy(r => r.Type))
    subZone.SetRecords(grp.Key, grp.ToList());

// after
var dnssecTypes = new[] { DnsResourceRecordType.DNSKEY, DnsResourceRecordType.RRSIG,
    DnsResourceRecordType.NSEC, DnsResourceRecordType.NSEC3PARAM, DnsResourceRecordType.NSEC3 };
foreach (var grp in importedRecords.GroupBy(r => r.Type))
    if (!dnssecTypes.Contains(grp.Key))
        subZone.SetRecords(grp.Key, grp.ToList());
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(type)) throw new InvalidOperationException("DNSSEC records are managed by the signing engine.");
zone.SetRecords(type, records);

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.SetRecords(type, records); }
catch (InvalidOperationException) when (IsDnssecRecordType(type)) { /* skip; signing engine owns these */ }

Prevention

When it happens

Trigger: subZone.SetRecords(DnsResourceRecordType.DNSKEY|RRSIG|NSEC|NSEC3PARAM|NSEC3, records) — e.g. importing a signed zone file verbatim, or a client trying to publish its own DNSKEY/DS material directly.

Common situations: AXFR-style import of an already-signed zone; manual key management attempts; test fixtures that include DNSSEC records.

Related errors


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