{"record":{"id":"ad3dfedfcd345b90","repo":"TechnitiumSoftware/DnsServer","slug":"failed-to-leave-cluster-a-primary-self-node-canno","errorCode":null,"errorMessage":"Failed to leave Cluster: a Primary self node cannot leave the Cluster.","messagePattern":"Failed to leave Cluster: a Primary self node cannot leave the Cluster\\.","errorType":"exception","errorClass":"DnsServerException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Cluster/ClusterManager.cs","lineNumber":1493,"sourceCode":"                try\n                {\n                    //logout from primary node\n                    await primaryNodeApiClient.LogoutAsync(cancellationToken);\n                }\n                catch\n                { }\n            }\n        }\n\n        public async Task LeaveClusterAsync(bool forceLeave)\n        {\n            if (!ClusterInitialized)\n                throw new DnsServerException(\"Failed to leave Cluster: the Cluster is not initialized.\");\n\n            ClusterNode primaryNode = GetPrimaryNode();\n\n            if (primaryNode.State == ClusterNodeState.Self)\n                throw new DnsServerException(\"Failed to leave Cluster: a Primary self node cannot leave the Cluster.\");\n\n            ClusterNode secondaryNode = GetSelfNode();\n\n            if (secondaryNode.Type != ClusterNodeType.Secondary)\n                throw new DnsServerException(\"Failed to leave Cluster: only Secondary nodes can leave the Cluster.\");\n\n            if (!forceLeave)\n            {\n                //delete self node from cluster on primary node\n                await primaryNode.DeleteSecondaryNodeAsync(secondaryNode);\n            }\n\n            //delete all cluster config\n            DeleteAllClusterConfig();\n        }\n\n        public async Task<ClusterNode> UpdatePrimaryNodeAsync(Uri primaryNodeUrl, IReadOnlyList<IPAddress> primaryNodeIpAddresses = null, int primaryNodeId = -1, CancellationToken cancellationToken = default)\n        {","sourceCodeStart":1475,"sourceCodeEnd":1511,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Cluster/ClusterManager.cs#L1475-L1511","documentation":"Thrown by LeaveClusterAsync when the primary node resolved by GetPrimaryNode() has State == ClusterNodeState.Self, i.e. this server is itself the cluster primary. A primary node cannot 'leave' its own cluster because leaving implies deregistering from a remote primary; the primary must instead transfer/demote. The guard stops an operator from orphaning the cluster.","triggerScenarios":"Calling LeaveClusterAsync from the server that currently holds the Primary role (GetPrimaryNode().State == Self).","commonSituations":"Operator on the primary node clicks 'Leave Cluster'; automation script runs the same leave routine on every cluster member including the primary; failover left this node as primary but the runbook still calls leave.","solutions":["Promote a secondary to primary first (PromoteToPrimaryNodeAsync on a secondary, or transfer primary), then leave from the now-secondary node.","If you intend to dissolve the cluster entirely, delete all cluster config / disband from the primary instead of calling leave.","In shared automation, branch on node type: only secondaries call LeaveClusterAsync."],"exampleFix":"// before: same leave path for every node\nawait clusterManager.LeaveClusterAsync(force: false);\n\n// after: only secondaries leave; primary must be demoted/transferred first\nif (clusterManager.GetPrimaryNode().State == ClusterNodeState.Self)\n    throw new InvalidOperationException(\"Demote/transfer primary before leaving.\");\nawait clusterManager.LeaveClusterAsync(force: false);","handlingStrategy":"validation","validationCode":"if (clusterManager.GetPrimaryNode().State == ClusterNodeState.Self)\n    throw new InvalidOperationException(\"Demote or transfer primary before leaving.\");\nawait clusterManager.LeaveClusterAsync(force: false);","typeGuard":"static bool IsSelfPrimary(ClusterManager cm)\n    => cm.ClusterInitialized && cm.GetPrimaryNode().State == ClusterNodeState.Self;","tryCatchPattern":"try { await clusterManager.LeaveClusterAsync(force: false); }\ncatch (DnsServerException ex) when (ex.Message.Contains(\"Primary self node cannot leave\"))\n{ /* instruct operator to promote a secondary first */ }","preventionTips":["Branch cluster runbooks on node role; only secondaries leave.","Before leaving, verify GetPrimaryNode() is not the self node.","For full cluster dissolution, use the primary-side disband path instead of leave."],"tags":["cluster","role-guard","primary-node","leave","technitium"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}