{"record":{"id":"b9ba9a8aa8ebc921","repo":"TechnitiumSoftware/DnsServer","slug":"failed-to-add-secondary-node-the-secondary-node-d","errorCode":null,"errorMessage":"Failed to add Secondary node: the Secondary node domain name must be a subdomain of the Cluster domain name.","messagePattern":"Failed to add Secondary node: the Secondary node domain name must be a subdomain of the Cluster domain name\\.","errorType":"exception","errorClass":"DnsServerException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Cluster/ClusterManager.cs","lineNumber":694,"sourceCode":"\n            if (!forceDelete && (_clusterNodes.Count > 1))\n                throw new DnsServerException(\"Failed to delete Cluster: please remove all Secondary nodes before deleting the Cluster.\");\n\n            DeleteAllClusterConfig();\n        }\n\n        public ClusterNode JoinCluster(int secondaryNodeId, Uri secondaryNodeUrl, IReadOnlyList<IPAddress> secondaryNodeIpAddresses, X509Certificate2 secondaryNodeCertificate)\n        {\n            if (!ClusterInitialized)\n                throw new DnsServerException(\"Failed to add Secondary node: the Cluster is not initialized.\");\n\n            if (GetSelfNode().Type != ClusterNodeType.Primary)\n                throw new DnsServerException(\"Failed to add Secondary node: only a Primary node can add a Secondary node to the Cluster.\");\n\n            string secondaryNodeDomain = secondaryNodeUrl.Host.ToLowerInvariant();\n\n            if (!secondaryNodeDomain.EndsWith(\".\" + _clusterDomain, StringComparison.OrdinalIgnoreCase))\n                throw new DnsServerException(\"Failed to add Secondary node: the Secondary node domain name must be a subdomain of the Cluster domain name.\");\n\n            IReadOnlyDictionary<int, ClusterNode> existingClusterNodes = _clusterNodes;\n\n            //validate for duplicate names\n            foreach (KeyValuePair<int, ClusterNode> existingClusterNode in existingClusterNodes)\n            {\n                if (existingClusterNode.Value.Name.Equals(secondaryNodeUrl.Host, StringComparison.OrdinalIgnoreCase))\n                    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.\");\n            }\n\n            //add secondary node to cluster nodes\n            ClusterNode secondaryNode = new ClusterNode(this, secondaryNodeId, secondaryNodeUrl, secondaryNodeIpAddresses, ClusterNodeType.Secondary, ClusterNodeState.Unknown);\n            Dictionary<int, ClusterNode> updatedClusterNodes = new Dictionary<int, ClusterNode>(existingClusterNodes.Count + 1);\n\n            foreach (KeyValuePair<int, ClusterNode> existingClusterNode in existingClusterNodes)\n                updatedClusterNodes[existingClusterNode.Value.Id] = existingClusterNode.Value;\n\n            if (!updatedClusterNodes.TryAdd(secondaryNode.Id, secondaryNode))","sourceCodeStart":676,"sourceCodeEnd":712,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Cluster/ClusterManager.cs#L676-L712","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","solutions":["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.","Re-issue the join request with a corrected secondaryNodeUrl whose Host ends with '.{clusterDomain}'."],"exampleFix":"// before\nvar nodeUrl = new Uri(\"https://ns2.other.com:5380/\");\n_clusterManager.JoinCluster(nodeId, nodeUrl, nodeIps, cert);\n// after — secondary domain must be a subdomain of the cluster domain\nvar nodeUrl = new Uri(\"https://ns2.cluster.example.com:5380/\");\n_clusterManager.JoinCluster(nodeId, nodeUrl, nodeIps, cert);","handlingStrategy":"validation","validationCode":"string clusterDomain = _dnsWebService.ClusterManager.ClusterDomain;\nstring secondaryHost = secondaryNodeUrl.Host.ToLowerInvariant();\nif (!secondaryHost.EndsWith(\".\" + clusterDomain, StringComparison.OrdinalIgnoreCase))\n    throw new InvalidOperationException($\"Secondary domain '{secondaryHost}' must be a subdomain of '{clusterDomain}'.\");\n_dnsWebService.ClusterManager.JoinCluster(nodeId, secondaryNodeUrl, nodeIps, cert);","typeGuard":"static bool IsValidSecondaryDomain(Uri secondaryUrl, string clusterDomain)\n    => secondaryUrl.Host.ToLowerInvariant().EndsWith(\".\" + clusterDomain, StringComparison.OrdinalIgnoreCase);","tryCatchPattern":"try\n{\n    _dnsWebService.ClusterManager.JoinCluster(nodeId, nodeUrl, nodeIps, cert);\n}\ncatch (DnsServerException ex) when (ex.Message.Contains(\"must be a subdomain of the Cluster domain\"))\n{\n    throw new InvalidOperationException($\"Set the Secondary's DNS Server Domain Name to a subdomain of the cluster domain and retry.\", ex);\n}","preventionTips":["Configure the Secondary server's DNS Server Domain Name to include the cluster domain suffix before joining.","Validate the URL host against the cluster domain in your orchestration script before calling JoinCluster.","Document the subdomain naming convention for all cluster nodes."],"tags":["cluster","join","domain-validation","naming"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}