TechnitiumSoftware/DnsServer · error · DnsServerException
Failed to add Secondary node: node ID already exists in the
Error message
Failed to add Secondary node: node ID already exists in the Cluster. Please try again.
What it means
Thrown by JoinCluster when Dictionary.TryAdd(secondaryNode.Id, secondaryNode) fails because the node ID already exists in the updatedClusterNodes dictionary. Node IDs must be unique; this is a defensive guard that catches ID collisions even though the earlier name-uniqueness loop passed.
Source
Thrown at DnsServerCore/Cluster/ClusterManager.cs:713
IReadOnlyDictionary<int, ClusterNode> existingClusterNodes = _clusterNodes;
//validate for duplicate names
foreach (KeyValuePair<int, ClusterNode> existingClusterNode in existingClusterNodes)
{
if (existingClusterNode.Value.Name.Equals(secondaryNodeUrl.Host, StringComparison.OrdinalIgnoreCase))
throw new DnsServerException("Failed to add Secondary node: A node with the same DNS Server Domain Name already exists in the Cluster. Please try again after changing the Secondary node's DNS Server Domain Name.");
}
//add secondary node to cluster nodes
ClusterNode secondaryNode = new ClusterNode(this, secondaryNodeId, secondaryNodeUrl, secondaryNodeIpAddresses, ClusterNodeType.Secondary, ClusterNodeState.Unknown);
Dictionary<int, ClusterNode> updatedClusterNodes = new Dictionary<int, ClusterNode>(existingClusterNodes.Count + 1);
foreach (KeyValuePair<int, ClusterNode> existingClusterNode in existingClusterNodes)
updatedClusterNodes[existingClusterNode.Value.Id] = existingClusterNode.Value;
if (!updatedClusterNodes.TryAdd(secondaryNode.Id, secondaryNode))
throw new DnsServerException("Failed to add Secondary node: node ID already exists in the Cluster. Please try again.");
if (updatedClusterNodes.Count > 255)
throw new DnsServerException("Failed to add Secondary node: a maximum of 255 nodes are supported by the Cluster.");
IReadOnlyDictionary<int, ClusterNode> originalValue = Interlocked.CompareExchange(ref _clusterNodes, updatedClusterNodes, existingClusterNodes);
if (!ReferenceEquals(originalValue, existingClusterNodes))
throw new DnsServerException("Failed to add Secondary node: please try again.");
secondaryNode.InitializeHeartbeatTimer();
//update cluster zone and save zone file
FindExistingRecordTtlValues(out uint nsTtl, out uint aTtl); //find existing record TTL values
AddClusterPrimaryZoneRecordsFor(secondaryNode, nsTtl, aTtl, secondaryNodeCertificate);
//update cluster catalog zone ACLs and save zone file
UpdateClusterCatalogZoneOptions();
//save all changesView on GitHub (pinned to d0484b6c1e)
Solutions
- Use a unique secondaryNodeId that does not match any existing node's Id.
- Delete the conflicting node (by Id) first if it is stale, then retry.
- If IDs are caller-allocated, generate them from a source guaranteed unique (e.g., RandomNumberGenerator.GetInt32) rather than hardcoding.
Example fix
// before _clusterManager.JoinCluster(42, nodeUrl, ips, cert); // 42 already exists // after _clusterManager.JoinCluster(RandomNumberGenerator.GetInt32(int.MaxValue), nodeUrl, ips, cert);
Defensive patterns
Strategy: validation
Validate before calling
// Ensure the node ID does not collide before joining
if (_dnsWebService.ClusterManager.ClusterNodes.Any(n => n.Key == nodeId))
throw new InvalidOperationException($"Node ID {nodeId} already exists; use a unique ID.");
_dnsWebService.ClusterManager.JoinCluster(nodeId, nodeUrl, nodeIps, cert); Type guard
static bool IsNodeIdUnique(ClusterManager cm, int nodeId)
=> !cm.ClusterNodes.Any(n => n.Key == nodeId); Try / catch
try
{
_clusterManager.JoinCluster(nodeId, nodeUrl, nodeIps, cert);
}
catch (DnsServerException ex) when (ex.Message.Contains("node ID already exists"))
{
// allocate a fresh ID and retry
throw;
} Prevention
- Generate node IDs with RandomNumberGenerator.GetInt32(int.MaxValue) to minimize collision probability.
- Check ClusterNodes for an existing ID before calling JoinCluster.
- Delete stale nodes by ID before reusing an ID.
When it happens
Trigger: JoinCluster is called with a secondaryNodeId that collides with an existing node's Id. The name check (error 149) compares hostnames, but this check compares integer IDs — so a different hostname with a reused ID triggers here.
Common situations: The caller reuses a secondaryNodeId from a previously removed node without verifying it was fully cleaned up; ID allocation logic on the caller side produced a duplicate; manual ID assignment collision.
Related errors
- Failed to add Secondary node: A node with the same DNS Serve
- Failed to add Secondary node: the Cluster is not initialized
- Failed to add Secondary node: only a Primary node can add a
- Failed to add Secondary node: the Secondary node domain name
- Failed to add Secondary node: a maximum of 255 nodes are sup
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/e24dd60c4760bc08.
Report an issue: GitHub.