{"record":{"id":"ff27fdd176451668","repo":"TechnitiumSoftware/DnsServer","slug":"failed-to-delete-cluster-only-a-primary-node-can","errorCode":null,"errorMessage":"Failed to delete Cluster: only a Primary node can delete the Cluster.","messagePattern":"Failed to delete Cluster: only a Primary node can delete the Cluster\\.","errorType":"exception","errorClass":"DnsServerException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Cluster/ClusterManager.cs","lineNumber":675,"sourceCode":"            //update cluster catalog zone ACLs, TSIG key name and save zone file\n            UpdateClusterCatalogZoneOptions(clusterCatalogZoneInfo);\n\n            //finalize\n            _dnsWebService.DnsServer.ServerDomain = selfPrimaryNode.Name;\n\n            //save all changes\n            _dnsWebService.DnsServer.SaveConfigFile(true);\n            _dnsWebService.AuthManager.SaveConfigFile(true);\n            SaveConfigFile();\n        }\n\n        public void DeleteCluster(bool forceDelete)\n        {\n            if (!ClusterInitialized)\n                throw new DnsServerException(\"Failed to delete Cluster: the Cluster is not initialized.\");\n\n            if (GetSelfNode().Type != ClusterNodeType.Primary)\n                throw new DnsServerException(\"Failed to delete Cluster: only a Primary node can delete the Cluster.\");\n\n            if (!forceDelete && (_clusterNodes.Count > 1))\n                throw new DnsServerException(\"Failed to delete Cluster: please remove all Secondary nodes before deleting the Cluster.\");\n\n            DeleteAllClusterConfig();\n        }\n\n        public ClusterNode JoinCluster(int secondaryNodeId, Uri secondaryNodeUrl, IReadOnlyList<IPAddress> secondaryNodeIpAddresses, X509Certificate2 secondaryNodeCertificate)\n        {\n            if (!ClusterInitialized)\n                throw new DnsServerException(\"Failed to add Secondary node: the Cluster is not initialized.\");\n\n            if (GetSelfNode().Type != ClusterNodeType.Primary)\n                throw new DnsServerException(\"Failed to add Secondary node: only a Primary node can add a Secondary node to the Cluster.\");\n\n            string secondaryNodeDomain = secondaryNodeUrl.Host.ToLowerInvariant();\n\n            if (!secondaryNodeDomain.EndsWith(\".\" + _clusterDomain, StringComparison.OrdinalIgnoreCase))","sourceCodeStart":657,"sourceCodeEnd":693,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Cluster/ClusterManager.cs#L657-L693","documentation":"Thrown by DeleteCluster when the current server's self node is not the Primary node. Only the Primary node holds authority to tear down the entire cluster because it owns the primary zone, the catalog zone, and the canonical node list. Secondary nodes are restricted from this destructive operation.","triggerScenarios":"DeleteCluster is called from a server whose GetSelfNode().Type is ClusterNodeType.Secondary rather than Primary. The guard at line 674 fires after the ClusterInitialized check passes.","commonSituations":"Running the delete API call or CLI command on the wrong server in a multi-node cluster (a Secondary instead of the Primary); load-balanced API requests hitting a Secondary backend.","solutions":["Identify the Primary node (use GetPrimaryNode or inspect the cluster node list) and run DeleteCluster on that server.","Check GetSelfNode().Type == ClusterNodeType.Primary before invoking DeleteCluster."],"exampleFix":"// before\n_dnsWebService.ClusterManager.DeleteCluster(false);\n// after\nif (_dnsWebService.ClusterManager.GetSelfNode().Type == ClusterNodeType.Primary)\n    _dnsWebService.ClusterManager.DeleteCluster(false);\nelse\n    throw new InvalidOperationException(\"Run this operation on the cluster Primary node.\");","handlingStrategy":"validation","validationCode":"if (_dnsWebService.ClusterManager.GetSelfNode().Type != ClusterNodeType.Primary)\n    throw new InvalidOperationException(\"DeleteCluster must run on the Primary node.\");\n_dnsWebService.ClusterManager.DeleteCluster(false);","typeGuard":"static bool IsPrimaryNode(ClusterManager cm)\n    => cm.ClusterInitialized && cm.GetSelfNode().Type == ClusterNodeType.Primary;","tryCatchPattern":"try\n{\n    _dnsWebService.ClusterManager.DeleteCluster(false);\n}\ncatch (DnsServerException ex) when (ex.Message.Contains(\"only a Primary node can delete\"))\n{\n    throw new InvalidOperationException(\"Redirect this request to the cluster Primary node.\", ex);\n}","preventionTips":["Route all cluster-deletion requests to the Primary node.","Check GetSelfNode().Type before invoking destructive cluster operations.","In a load-balanced setup, ensure cluster-management endpoints are pinned to the Primary."],"tags":["cluster","authorization","primary-node","delete","rbac"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}