{"record":{"id":"3bbd64c4b04e4642","repo":"TechnitiumSoftware/DnsServer","slug":"cannot-add-dnssec-record-3bbd64","errorCode":null,"errorMessage":"Cannot add DNSSEC record.","messagePattern":"Cannot add DNSSEC record\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Zones/PrimarySubDomainZone.cs","lineNumber":134,"sourceCode":"                    case DnsResourceRecordType.APP:\n                        throw new DnsServerException(\"The record type is not supported by DNSSEC signed primary zones.\");\n\n                    default:\n                        if (record.GetAuthGenericRecordInfo().Disabled)\n                            throw new DnsServerException(\"Cannot add record: disabling records in a signed zones is not supported.\");\n\n                        break;\n                }\n            }\n\n            switch (record.Type)\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 add DNSSEC record.\");\n\n                case DnsResourceRecordType.FWD:\n                    throw new DnsServerException(\"The record type is not supported by primary zones.\");\n\n                default:\n                    if (record.OriginalTtlValue > _primaryZone.GetZoneSoaExpire())\n                        throw new DnsServerException(\"Cannot add record: TTL cannot be greater than SOA EXPIRE.\");\n\n                    AddRecord(record, out IReadOnlyList<DnsResourceRecord> addedRecords, out IReadOnlyList<DnsResourceRecord> deletedRecords);\n\n                    if (addedRecords.Count > 0)\n                    {\n                        _primaryZone.CommitAndIncrementSerial(deletedRecords, addedRecords);\n\n                        if (_primaryZone.DnssecStatus != AuthZoneDnssecStatus.Unsigned)\n                            _primaryZone.UpdateDnssecRecordsFor(this, record.Type);\n\n                        _primaryZone.TriggerNotify();","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Zones/PrimarySubDomainZone.cs#L116-L152","documentation":"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.","triggerScenarios":"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.","commonSituations":"Signed-zone import that does not strip DNSSEC records; manual key/DS publishing attempts; test data containing RRSIG records.","solutions":["Never AddRecord a DNSSEC type; let the signing engine generate DNSKEY/RRSIG/NSEC/NSEC3.","Strip DNSSEC record types from imports before adding records one by one.","If re-signing is the goal, trigger it through the DNSSEC API, not by adding records."],"exampleFix":"// before\nforeach (var rec in importedRecords)\n    zone.AddRecord(rec);\n\n// after\nvar dnssecTypes = new HashSet<DnsResourceRecordType> {\n    DnsResourceRecordType.DNSKEY, DnsResourceRecordType.RRSIG,\n    DnsResourceRecordType.NSEC, DnsResourceRecordType.NSEC3PARAM, DnsResourceRecordType.NSEC3 };\nforeach (var rec in importedRecords)\n    if (!dnssecTypes.Contains(rec.Type))\n        zone.AddRecord(rec);","handlingStrategy":"validation","validationCode":"var dnssecTypes = new HashSet<DnsResourceRecordType> { DnsResourceRecordType.DNSKEY, DnsResourceRecordType.RRSIG, DnsResourceRecordType.NSEC, DnsResourceRecordType.NSEC3PARAM, DnsResourceRecordType.NSEC3 };\nif (dnssecTypes.Contains(record.Type)) throw new InvalidOperationException(\"DNSSEC records are managed by the signing engine.\");\nzone.AddRecord(record);","typeGuard":"static bool IsDnssecRecordType(DnsResourceRecordType t) => t == DnsResourceRecordType.DNSKEY || t == DnsResourceRecordType.RRSIG || t == DnsResourceRecordType.NSEC || t == DnsResourceRecordType.NSEC3PARAM || t == DnsResourceRecordType.NSEC3;","tryCatchPattern":"try { zone.AddRecord(record); }\ncatch (InvalidOperationException) when (IsDnssecRecordType(record.Type)) { /* skip; signing engine owns these */ }","preventionTips":["Strip DNSSEC record types from imports before AddRecord loops.","Reserve DNSKEY/RRSIG/NSEC* authorship to the signing engine.","Publish trust anchors via DS at the parent, never DNSKEY at the child via AddRecord."],"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"}