{"record":{"id":"374b2161a6bde524","repo":"TechnitiumSoftware/DnsServer","slug":"failed-to-update-primary-node-the-cluster-seconda","errorCode":null,"errorMessage":"Failed to update Primary node: the Cluster Secondary Catalog zone '{clusterCatalogDomain}' does not exists.","messagePattern":"Failed to update Primary node: the Cluster Secondary Catalog zone '(.+?)' does not exists\\.","errorType":"exception","errorClass":"DnsServerException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Cluster/ClusterManager.cs","lineNumber":1586,"sourceCode":"                SaveConfigFile();\n            }\n\n            //validate for duplicate names\n            foreach (KeyValuePair<int, ClusterNode> clusterNode in _clusterNodes)\n            {\n                if (clusterNode.Key == primaryNode.Id)\n                    continue; //skip self\n\n                if (clusterNode.Value.Name.Equals(primaryNodeUrl.Host, StringComparison.OrdinalIgnoreCase))\n                    throw new DnsServerException(\"Failed to update Primary node: the Primary node's domain name already exists in the Cluster. Please try again after changing the Primary DNS Server's domain name.\");\n            }\n\n            //get cluster secondary catalog zone\n            string clusterCatalogDomain = \"cluster-catalog.\" + _clusterDomain;\n\n            AuthZoneInfo clusterSecondaryCatalogZoneInfo = _dnsWebService.DnsServer.AuthZoneManager.GetAuthZoneInfo(clusterCatalogDomain);\n            if (clusterSecondaryCatalogZoneInfo is null)\n                throw new DnsServerException($\"Failed to update Primary node: the Cluster Secondary Catalog zone '{clusterCatalogDomain}' does not exists.\");\n\n            //update primary node\n            primaryNode.UpdateNode(primaryNodeUrl, primaryNodeIpAddresses);\n\n            //update cluster catalog zone's primary name server\n            clusterSecondaryCatalogZoneInfo.PrimaryNameServerAddresses = primaryNodeIpAddresses.Convert(delegate (IPAddress ipAddress) { return new NameServerAddress(primaryNodeUrl.Host, ipAddress); });\n\n            //save all changes\n            _dnsWebService.DnsServer.AuthZoneManager.SaveZoneFile(clusterSecondaryCatalogZoneInfo.Name);\n            SaveConfigFile();\n\n            //trigger config and zone refresh\n            TriggerRefreshForConfig(CONFIG_REFRESH_TIMER_INTERVAL);\n\n            return primaryNode;\n        }\n\n        public void TriggerRefreshForConfig(IReadOnlyCollection<string> configRefreshIncludeZones = null)","sourceCodeStart":1568,"sourceCodeEnd":1604,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Cluster/ClusterManager.cs#L1568-L1604","documentation":"Thrown by UpdatePrimaryNodeAsync when GetAuthZoneInfo('cluster-catalog.<clusterDomain>') returns null. The update flow needs to rewrite the secondary catalog zone's primary name server addresses, so the zone must exist; its absence means cluster zone state is inconsistent or was deleted out-of-band.","triggerScenarios":"UpdatePrimaryNodeAsync called when the 'cluster-catalog.<clusterDomain>' secondary catalog zone has been deleted, renamed, or never created on this node.","commonSituations":"Operator manually deleted the catalog zone; a prior failed promotion/join cleaned up zones partially; zone file corrupted on disk; the cluster domain changed but the catalog zone was not recreated.","solutions":["Re-create the cluster-catalog zone by rejoining the cluster (LeaveClusterAsync(true) then JoinClusterAsync) to rebuild consistent zone state.","Restore the catalog zone from a known-good zone file backup.","Confirm _clusterDomain is correct and the zone name spelling matches; recreate if a rename occurred."],"exampleFix":"// before\nawait clusterManager.UpdatePrimaryNodeAsync(primaryUrl, ips);\n\n// after: verify zone presence, rebuild if missing\nvar catalog = \"cluster-catalog.\" + clusterManager.ClusterDomain;\nif (dnsServer.AuthZoneManager.GetAuthZoneInfo(catalog) is null)\n{\n    await clusterManager.LeaveClusterAsync(force: true);\n    await clusterManager.JoinClusterAsync(primaryUrl, creds, ct);\n}\nawait clusterManager.UpdatePrimaryNodeAsync(primaryUrl, ips);","handlingStrategy":"validation","validationCode":"string catalog = \"cluster-catalog.\" + clusterManager.ClusterDomain;\nif (dnsServer.AuthZoneManager.GetAuthZoneInfo(catalog) is null)\n    throw new InvalidOperationException($\"Missing catalog zone '{catalog}'; rebuild cluster state.\");\nawait clusterManager.UpdatePrimaryNodeAsync(primaryUrl, ips);","typeGuard":"static bool CatalogZoneExists(DnsWebService svc, string clusterDomain)\n    => svc.DnsServer.AuthZoneManager.GetAuthZoneInfo(\"cluster-catalog.\" + clusterDomain) is not null;","tryCatchPattern":"try { await clusterManager.UpdatePrimaryNodeAsync(primaryUrl, ips); }\ncatch (DnsServerException ex) when (ex.Message.Contains(\"Catalog zone\") && ex.Message.Contains(\"does not exists\"))\n{\n    await clusterManager.LeaveClusterAsync(force: true);\n    await clusterManager.JoinClusterAsync(primaryUrl, creds, ct);\n}","preventionTips":["Never manually delete the cluster-catalog zone on a clustered node.","After any failed join/leave/promote, verify both cluster zones still exist.","Keep zone file backups so a missing zone can be restored without full rejoin."],"tags":["cluster","dns-zone","update-primary","missing-state","technitium"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}