{"record":{"id":"493e32da2d08fbdb","repo":"TechnitiumSoftware/DnsServer","slug":"failed-to-delete-secondary-node-the-specified-nod-493e32","errorCode":null,"errorMessage":"Failed to delete Secondary node: the specified node is the Cluster Primary node and cannot be deleted.","messagePattern":"Failed to delete Secondary node: the specified node is the Cluster Primary node and cannot be deleted\\.","errorType":"exception","errorClass":"DnsServerException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Cluster/ClusterManager.cs","lineNumber":779,"sourceCode":"            return secondaryNode;\n        }\n\n        public ClusterNode DeleteSecondaryNode(int secondaryNodeId)\n        {\n            if (!ClusterInitialized)\n                throw new DnsServerException(\"Failed to delete Secondary node: the Cluster is not initialized.\");\n\n            if (GetSelfNode().Type != ClusterNodeType.Primary)\n                throw new DnsServerException(\"Failed to delete Secondary node: only a Primary node can delete a Secondary node from the Cluster.\");\n\n            //find existing secondary node\n            IReadOnlyDictionary<int, ClusterNode> existingClusterNodes = _clusterNodes;\n\n            if (!existingClusterNodes.TryGetValue(secondaryNodeId, out ClusterNode secondaryNode))\n                throw new DnsServerException(\"Failed to delete Secondary node: the specified node does not exist in the Cluster.\");\n\n            if (secondaryNode.Type == ClusterNodeType.Primary)\n                throw new DnsServerException(\"Failed to delete Secondary node: the specified node is the Cluster Primary node and cannot be deleted.\");\n\n            //delete secondary node from cluster nodes\n            Dictionary<int, ClusterNode> updatedClusterNodes = new Dictionary<int, ClusterNode>(existingClusterNodes.Count - 1);\n\n            foreach (KeyValuePair<int, ClusterNode> existingClusterNode in existingClusterNodes)\n            {\n                if (existingClusterNode.Key == secondaryNodeId)\n                    continue;\n\n                updatedClusterNodes[existingClusterNode.Key] = existingClusterNode.Value;\n            }\n\n            IReadOnlyDictionary<int, ClusterNode> originalValue = Interlocked.CompareExchange(ref _clusterNodes, updatedClusterNodes, existingClusterNodes);\n            if (!ReferenceEquals(originalValue, existingClusterNodes))\n                throw new InvalidOperationException(\"Failed to delete Secondary node: please try again.\");\n\n            secondaryNode.Dispose();\n","sourceCodeStart":761,"sourceCodeEnd":797,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Cluster/ClusterManager.cs#L761-L797","documentation":"Thrown by DeleteSecondaryNode when the supplied node ID resolves to the cluster's Primary node (ClusterNodeType.Primary). The cluster topology requires exactly one Primary, and deleting it would orphan the cluster, so the operation is rejected. The check fires after the node lookup succeeds, so the ID is valid but of the wrong role.","triggerScenarios":"Calling DeleteSecondaryNode(primaryNodeId) on the Primary; an automation loop that iterates every node ID and calls delete on each; passing GetSelfNode().Id by accident.","commonSituations":"Admin UI or script that does not filter the node list by type before deletion; copy-pasting the wrong ID; treating 'remove this server from cluster' as 'delete node' on the Primary itself.","solutions":["Pass the Secondary node's ID, not the Primary's - obtain it from the live cluster state and filter Type == Secondary","Exclude the Primary (GetSelfNode()) from any batch-delete loop","To remove the Primary, demote/leave the cluster instead of calling DeleteSecondaryNode"],"exampleFix":"// before\nforeach (var id in allNodeIds) mgr.DeleteSecondaryNode(id);\n\n// after\nforeach (var node in clusterNodes.Where(n => n.Type == ClusterNodeType.Secondary))\n    mgr.DeleteSecondaryNode(node.Id);","handlingStrategy":"validation","validationCode":"var target = clusterState.ClusterNodes.FirstOrDefault(n => n.Id == secondaryNodeId);\nif (target is null) throw new ArgumentException(\"node not found\");\nif (target.Type == ClusterNodeType.Primary)\n    throw new InvalidOperationException(\"Cannot delete the Primary node; choose a Secondary node.\");\nclusterManager.DeleteSecondaryNode(secondaryNodeId);","typeGuard":null,"tryCatchPattern":"try { clusterManager.DeleteSecondaryNode(id); }\ncatch (DnsServerException ex) when (ex.Message.Contains(\"Cluster Primary node\"))\n{ /* skip primary; log and continue */ }","preventionTips":["Derive the node list from live cluster state and filter to Secondary before deleting","Never feed GetSelfNode().Id into DeleteSecondaryNode"],"tags":["cluster","primary-node","validation","dns-server"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}