TechnitiumSoftware/DnsServer · error · DnsServerException
Failed to ask Secondary node to leave: the specified node is
Error message
Failed to ask Secondary node to leave: the specified node is the Cluster Primary node and cannot be removed.
What it means
Thrown by AskSecondaryNodeToLeaveClusterAsync when the node resolved by secondaryNodeId has Type == ClusterNodeType.Primary. The Primary node cannot be asked to leave — it is the authoritative owner of the cluster and must be removed via DeleteCluster instead. This guard runs after the node-exists check.
Source
Thrown at DnsServerCore/Cluster/ClusterManager.cs:756
TriggerRecordUpdateForClusterCatalogMemberZones();
return secondaryNode;
}
public async Task<ClusterNode> AskSecondaryNodeToLeaveClusterAsync(int secondaryNodeId)
{
if (!ClusterInitialized)
throw new DnsServerException("Failed to ask Secondary node to leave: the Cluster is not initialized.");
if (GetSelfNode().Type != ClusterNodeType.Primary)
throw new DnsServerException("Failed to ask Secondary node to leave: only a Primary node can ask a Secondary node to leave the Cluster.");
//find existing secondary node
if (!_clusterNodes.TryGetValue(secondaryNodeId, out ClusterNode secondaryNode))
throw new DnsServerException("Failed to ask Secondary node to leave: the specified node does not exist in the Cluster.");
if (secondaryNode.Type == ClusterNodeType.Primary)
throw new DnsServerException("Failed to ask Secondary node to leave: the specified node is the Cluster Primary node and cannot be removed.");
//ask secondary node to leave the cluster
await secondaryNode.AskSecondaryNodeToLeaveClusterAsync();
return secondaryNode;
}
public ClusterNode DeleteSecondaryNode(int secondaryNodeId)
{
if (!ClusterInitialized)
throw new DnsServerException("Failed to delete Secondary node: the Cluster is not initialized.");
if (GetSelfNode().Type != ClusterNodeType.Primary)
throw new DnsServerException("Failed to delete Secondary node: only a Primary node can delete a Secondary node from the Cluster.");
//find existing secondary node
IReadOnlyDictionary<int, ClusterNode> existingClusterNodes = _clusterNodes;
View on GitHub (pinned to d0484b6c1e)
Solutions
- Pass a Secondary node's ID — use the node list to find one with Type == ClusterNodeType.Secondary.
- If you want to remove the Primary, use DeleteCluster instead.
- Filter the target node by type before calling: ensure secondaryNode.Type != ClusterNodeType.Primary.
Example fix
// before await _clusterManager.AskSecondaryNodeToLeaveClusterAsync(primaryNodeId); // after — target a Secondary, not the Primary var target = _clusterManager.ClusterNodes.First(n => n.Value.Type == ClusterNodeType.Secondary); await _clusterManager.AskSecondaryNodeToLeaveClusterAsync(target.Key);
Defensive patterns
Strategy: validation
Validate before calling
// Ensure the target is a Secondary, not the Primary
var target = _dnsWebService.ClusterManager.ClusterNodes
.FirstOrDefault(n => n.Key == nodeId);
if (target.Value is null)
throw new InvalidOperationException("Node not found.");
if (target.Value.Type == ClusterNodeType.Primary)
throw new InvalidOperationException("Cannot ask the Primary to leave; use DeleteCluster instead.");
await _dnsWebService.ClusterManager.AskSecondaryNodeToLeaveClusterAsync(nodeId); Type guard
static bool IsSecondaryNode(ClusterManager cm, int nodeId)
=> cm.ClusterNodes.TryGetValue(nodeId, out var node) && node.Type == ClusterNodeType.Secondary; Try / catch
try
{
await _clusterManager.AskSecondaryNodeToLeaveClusterAsync(nodeId);
}
catch (DnsServerException ex) when (ex.Message.Contains("Cluster Primary node and cannot be removed"))
{
throw new InvalidOperationException("The target is the Primary node. Use DeleteCluster to remove the entire cluster.", ex);
} Prevention
- Filter the node list to Secondaries only when presenting leave options.
- Validate the target node's Type is Secondary before calling.
- Use DeleteCluster (not leave) when the intent is to remove the Primary.
When it happens
Trigger: AskSecondaryNodeToLeaveClusterAsync is called with the Primary node's ID. The lookup at line 752 succeeds but the type check at line 755 (secondaryNode.Type == ClusterNodeType.Primary) fires.
Common situations: Client or UI passes the Primary node's ID by mistake; iterating over all node IDs and calling leave without filtering by type; confusion between leave (for Secondaries) and delete (for the whole cluster).
Related errors
- Failed to ask Secondary node to leave: only a Primary node c
- Failed to leave Cluster: a Primary self node cannot leave th
- Failed to delete Cluster: only a Primary node can delete the
- Failed to add Secondary node: only a Primary node can add a
- Failed to ask Secondary node to leave: the Cluster is not in
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/c9021ad95532d18f.
Report an issue: GitHub.