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 changes

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Use a unique secondaryNodeId that does not match any existing node's Id.
  2. Delete the conflicting node (by Id) first if it is stale, then retry.
  3. 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

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


AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13). Data as JSON: /api/errors/e24dd60c4760bc08. Report an issue: GitHub.