TechnitiumSoftware/DnsServer · error · DnsServerException

Failed to add Secondary node: the Secondary node domain name

Error message

Failed to add Secondary node: the Secondary node domain name must be a subdomain of the Cluster domain name.

What it means

Thrown by JoinCluster when the Secondary node's URL host does not end with '.' + the cluster domain. This ensures every Secondary's DNS Server Domain Name is a proper subdomain of the cluster domain so that NS/A/AAAA records can be placed correctly within the cluster primary zone.

Source

Thrown at DnsServerCore/Cluster/ClusterManager.cs:694

            if (!forceDelete && (_clusterNodes.Count > 1))
                throw new DnsServerException("Failed to delete Cluster: please remove all Secondary nodes before deleting the Cluster.");

            DeleteAllClusterConfig();
        }

        public ClusterNode JoinCluster(int secondaryNodeId, Uri secondaryNodeUrl, IReadOnlyList<IPAddress> secondaryNodeIpAddresses, X509Certificate2 secondaryNodeCertificate)
        {
            if (!ClusterInitialized)
                throw new DnsServerException("Failed to add Secondary node: the Cluster is not initialized.");

            if (GetSelfNode().Type != ClusterNodeType.Primary)
                throw new DnsServerException("Failed to add Secondary node: only a Primary node can add a Secondary node to the Cluster.");

            string secondaryNodeDomain = secondaryNodeUrl.Host.ToLowerInvariant();

            if (!secondaryNodeDomain.EndsWith("." + _clusterDomain, StringComparison.OrdinalIgnoreCase))
                throw new DnsServerException("Failed to add Secondary node: the Secondary node domain name must be a subdomain of the Cluster domain name.");

            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))

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Set the Secondary server's DNS Server Domain Name to a subdomain of the cluster domain (e.g., 'ns2.cluster.example.com' for cluster domain 'cluster.example.com') before joining.
  2. Re-issue the join request with a corrected secondaryNodeUrl whose Host ends with '.{clusterDomain}'.

Example fix

// before
var nodeUrl = new Uri("https://ns2.other.com:5380/");
_clusterManager.JoinCluster(nodeId, nodeUrl, nodeIps, cert);
// after — secondary domain must be a subdomain of the cluster domain
var nodeUrl = new Uri("https://ns2.cluster.example.com:5380/");
_clusterManager.JoinCluster(nodeId, nodeUrl, nodeIps, cert);
Defensive patterns

Strategy: validation

Validate before calling

string clusterDomain = _dnsWebService.ClusterManager.ClusterDomain;
string secondaryHost = secondaryNodeUrl.Host.ToLowerInvariant();
if (!secondaryHost.EndsWith("." + clusterDomain, StringComparison.OrdinalIgnoreCase))
    throw new InvalidOperationException($"Secondary domain '{secondaryHost}' must be a subdomain of '{clusterDomain}'.");
_dnsWebService.ClusterManager.JoinCluster(nodeId, secondaryNodeUrl, nodeIps, cert);

Type guard

static bool IsValidSecondaryDomain(Uri secondaryUrl, string clusterDomain)
    => secondaryUrl.Host.ToLowerInvariant().EndsWith("." + clusterDomain, StringComparison.OrdinalIgnoreCase);

Try / catch

try
{
    _dnsWebService.ClusterManager.JoinCluster(nodeId, nodeUrl, nodeIps, cert);
}
catch (DnsServerException ex) when (ex.Message.Contains("must be a subdomain of the Cluster domain"))
{
    throw new InvalidOperationException($"Set the Secondary's DNS Server Domain Name to a subdomain of the cluster domain and retry.", ex);
}

Prevention

When it happens

Trigger: JoinCluster is called with a secondaryNodeUrl whose Host property (lowercased) does not satisfy secondaryNodeDomain.EndsWith('.' + _clusterDomain). For example, joining 'ns2.other.com' to a cluster domain of 'cluster.example.com'.

Common situations: The Secondary server's DNS Server Domain Name was not set to include the cluster domain suffix before the join attempt; copy-paste error in the URL; using an IP-based or unrelated hostname URL.

Related errors


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