{"record":{"id":"d097a032bdbc3283","repo":"TechnitiumSoftware/DnsServer","slug":"cannot-set-dnssec-records-d097a0","errorCode":null,"errorMessage":"Cannot set DNSSEC records.","messagePattern":"Cannot set DNSSEC records\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Zones/PrimarySubDomainZone.cs","lineNumber":87,"sourceCode":"                            if (record.GetAuthGenericRecordInfo().Disabled)\n                                throw new DnsServerException(\"Cannot set records: disabling records in a signed zones is not supported.\");\n                        }\n\n                        break;\n                }\n            }\n\n            switch (type)\n            {\n                case DnsResourceRecordType.SOA:\n                    throw new InvalidOperationException(\"Cannot set SOA record on sub domain.\");\n\n                case DnsResourceRecordType.DNSKEY:\n                case DnsResourceRecordType.RRSIG:\n                case DnsResourceRecordType.NSEC:\n                case DnsResourceRecordType.NSEC3PARAM:\n                case DnsResourceRecordType.NSEC3:\n                    throw new InvalidOperationException(\"Cannot set DNSSEC records.\");\n\n                case DnsResourceRecordType.FWD:\n                    throw new DnsServerException(\"The record type is not supported by primary zones.\");\n\n                default:\n                    if (records[0].OriginalTtlValue > _primaryZone.GetZoneSoaExpire())\n                        throw new DnsServerException(\"Cannot set records: TTL cannot be greater than SOA EXPIRE.\");\n\n                    if (!TrySetRecords(type, records, out IReadOnlyList<DnsResourceRecord> deletedRecords))\n                        throw new DnsServerException(\"Cannot set records. Please try again.\");\n\n                    _primaryZone.CommitAndIncrementSerial(deletedRecords, records);\n\n                    if (_primaryZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned)\n                        _primaryZone.UpdateDnssecRecordsFor(this, type);\n\n                    _primaryZone.TriggerNotify();\n                    break;","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Zones/PrimarySubDomainZone.cs#L69-L105","documentation":"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.","triggerScenarios":"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.","commonSituations":"AXFR-style import of an already-signed zone; manual key management attempts; test fixtures that include DNSSEC records.","solutions":["Exclude DNSSEC record types (DNSKEY, RRSIG, NSEC, NSEC3PARAM, NSEC3) from any SetRecords call; let the signing engine produce them.","When importing a signed zone, import only the user records (A, AAAA, MX, etc.) and re-sign on the server.","If publishing a trust anchor, use the DS record at the parent, not DNSKEY at the child."],"exampleFix":"// before\nforeach (var grp in importedRecords.GroupBy(r => r.Type))\n    subZone.SetRecords(grp.Key, grp.ToList());\n\n// after\nvar dnssecTypes = new[] { DnsResourceRecordType.DNSKEY, DnsResourceRecordType.RRSIG,\n    DnsResourceRecordType.NSEC, DnsResourceRecordType.NSEC3PARAM, DnsResourceRecordType.NSEC3 };\nforeach (var grp in importedRecords.GroupBy(r => r.Type))\n    if (!dnssecTypes.Contains(grp.Key))\n        subZone.SetRecords(grp.Key, grp.ToList());","handlingStrategy":"validation","validationCode":"var dnssecTypes = new HashSet<DnsResourceRecordType> { DnsResourceRecordType.DNSKEY, DnsResourceRecordType.RRSIG, DnsResourceRecordType.NSEC, DnsResourceRecordType.NSEC3PARAM, DnsResourceRecordType.NSEC3 };\nif (dnssecTypes.Contains(type)) throw new InvalidOperationException(\"DNSSEC records are managed by the signing engine.\");\nzone.SetRecords(type, records);","typeGuard":"static bool IsDnssecRecordType(DnsResourceRecordType t) => t == DnsResourceRecordType.DNSKEY || t == DnsResourceRecordType.RRSIG || t == DnsResourceRecordType.NSEC || t == DnsResourceRecordType.NSEC3PARAM || t == DnsResourceRecordType.NSEC3;","tryCatchPattern":"try { zone.SetRecords(type, records); }\ncatch (InvalidOperationException) when (IsDnssecRecordType(type)) { /* skip; signing engine owns these */ }","preventionTips":["Strip DNSSEC record types from all imports before calling SetRecords.","Let the server's signing engine be the sole writer of DNSKEY/RRSIG/NSEC*.","Publish trust anchors via DS at the parent, not DNSKEY at the child."],"tags":["dns","primary-zone","subdomain","dnssec","invalidoperationexception"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}