{"record":{"id":"04864fd42677ef18","repo":"TechnitiumSoftware/DnsServer","slug":"the-private-keys-must-contain-at-least-one-zone-si","errorCode":null,"errorMessage":"The private keys must contain at least one Zone Signing Key.","messagePattern":"The private keys must contain at least one Zone Signing Key\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/Zones/PrimaryZone.cs","lineNumber":510,"sourceCode":"            foreach (DnssecPrivateKey dnssecPrivateKey in dnssecPrivateKeys)\n            {\n                switch (dnssecPrivateKey.KeyType)\n                {\n                    case DnssecPrivateKeyType.KeySigningKey:\n                        foundKsk = true;\n                        break;\n\n                    case DnssecPrivateKeyType.ZoneSigningKey:\n                        foundZsk = true;\n                        break;\n                }\n            }\n\n            if (!foundKsk)\n                throw new ArgumentException(\"The private keys must contain at least one Key Signing Key.\", nameof(dnssecPrivateKeys));\n\n            if (!foundZsk)\n                throw new ArgumentException(\"The private keys must contain at least one Zone Signing Key.\", nameof(dnssecPrivateKeys));\n\n            //load dnssec private keys\n            _dnssecPrivateKeys = new Dictionary<ushort, DnssecPrivateKey>(dnssecPrivateKeys.Count);\n\n            foreach (DnssecPrivateKey dnssecPrivateKey in dnssecPrivateKeys)\n                _dnssecPrivateKeys.Add(dnssecPrivateKey.KeyTag, dnssecPrivateKey);\n\n            //start zone signing\n            List<DnsResourceRecord> addedRecords = new List<DnsResourceRecord>();\n            List<DnsResourceRecord> deletedRecords = new List<DnsResourceRecord>();\n\n            try\n            {\n                IReadOnlyList<AuthZone> zones = _dnsServer.AuthZoneManager.GetApexZoneWithSubDomainZones(_name);\n\n                //find max record ttl in zone\n                uint maxRecordTtl = 0;\n","sourceCodeStart":492,"sourceCodeEnd":528,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/Zones/PrimaryZone.cs#L492-L528","documentation":"Companion to error 516: thrown by the collection overload PrimaryZone.SignZone when no key in dnssecPrivateKeys has KeyType == ZoneSigningKey. Without a ZSK the non-DNSKEY RRsets cannot be signed, so the guard raises ArgumentException(nameof(dnssecPrivateKeys)) right after the KSK check.","triggerScenarios":"zone.SignZone(kskOnlyList, ...) — a collection with only KeySigningKey entries.","commonSituations":"Creating one KSK and forgetting the ZSK; key-roll scripts that provision only the trust anchor.","solutions":["Include at least one ZoneSigningKey in the collection.","Validate both roles are present before signing.","Use the (ksk, zsk) overload for the common single-KSK/single-ZSK case."],"exampleFix":"// before\nzone.SignZone(new[] { ksk }, ttl, false);\n\n// after\nzone.SignZone(new[] { ksk, zsk }, ttl, false);","handlingStrategy":"validation","validationCode":"if (!dnssecPrivateKeys.Any(k => k.KeyType == DnssecPrivateKeyType.ZoneSigningKey))\n    throw new ArgumentException(\"At least one ZSK required.\");\nzone.SignZone(dnssecPrivateKeys, ttl, useNSec3);","typeGuard":"static bool HasZsk(IEnumerable<DnssecPrivateKey> ks) => ks.Any(k => k.KeyType == DnssecPrivateKeyType.ZoneSigningKey);","tryCatchPattern":null,"preventionTips":["Ensure the key set contains at least one ZSK.","Provision a ZSK whenever you create a KSK.","Validate both roles at key-collection build time."],"tags":["csharp","dns","dnssec","validation","technitium-dns"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}