TechnitiumSoftware/DnsServer · error · DnsServerException
Failed to update Cluster options: only the Primary node can
Error message
Failed to update Cluster options: only the Primary node can update the Cluster options.
What it means
UpdateClusterOptions may only be called on the cluster Primary (GetSelfNode().Type == Primary). Cluster options are Primary-owned and replicated to Secondaries.
Source
Thrown at DnsServerCore/Cluster/ClusterManager.cs:905
allowedZones: true,
blockedZones: true,
blockLists: true,
apps: true,
scopes: false,
stats: false,
logs: false,
isConfigTransfer: true,
ifModifiedSince: ifModifiedSince,
includeZones: includeZones);
}
public void UpdateClusterOptions(ushort heartbeatRefreshIntervalSeconds, ushort heartbeatRetryIntervalSeconds, ushort configRefreshIntervalSeconds, ushort configRetryIntervalSeconds)
{
if (!ClusterInitialized)
throw new DnsServerException("Failed to update Cluster options: the Cluster is not initialized.");
if (GetSelfNode().Type != ClusterNodeType.Primary)
throw new DnsServerException("Failed to update Cluster options: only the Primary node can update the Cluster options.");
if ((heartbeatRefreshIntervalSeconds < 10) || (heartbeatRefreshIntervalSeconds > 300))
throw new ArgumentOutOfRangeException(nameof(heartbeatRefreshIntervalSeconds));
if ((heartbeatRetryIntervalSeconds < 10) || (heartbeatRetryIntervalSeconds > 300))
throw new ArgumentOutOfRangeException(nameof(heartbeatRetryIntervalSeconds));
if ((configRefreshIntervalSeconds < 30) || (configRefreshIntervalSeconds > 3600))
throw new ArgumentOutOfRangeException(nameof(configRefreshIntervalSeconds));
if ((configRetryIntervalSeconds < 30) || (configRetryIntervalSeconds > 3600))
throw new ArgumentOutOfRangeException(nameof(configRetryIntervalSeconds));
if (configRefreshIntervalSeconds <= heartbeatRefreshIntervalSeconds)
throw new ArgumentException("Failed to update Cluster options: The config refresh interval must be greater than the heartbeat refresh interval.");
bool changed = false;
View on GitHub (pinned to d0484b6c1e)
Solutions
- Send option updates to the current Primary
- Rediscover the Primary after failover
- Use role-aware routing in front of the API
Example fix
// before
clusterManager.UpdateClusterOptions(hb, hbRetry, cfg, cfgRetry);
// after
if (clusterManager.GetSelfNode().Type != ClusterNodeType.Primary)
return Redirect("Update options on the Primary node.");
clusterManager.UpdateClusterOptions(hb, hbRetry, cfg, cfgRetry); Defensive patterns
Strategy: validation
Validate before calling
if (clusterManager.GetSelfNode().Type != ClusterNodeType.Primary)
return BadRequest("Only the Primary can update cluster options.");
clusterManager.UpdateClusterOptions(hb, hbRetry, cfg, cfgRetry); Try / catch
catch (DnsServerException ex) when (ex.Message.Contains("only the Primary node can update the Cluster options"))
{ /* rediscover Primary and retry */ } Prevention
- Route option changes through the Primary role, not a fixed member
- Re-check the Primary after failover
When it happens
Trigger: Calling the tuning endpoint on a Secondary; an LB routing to a non-Primary member; a demotion after the client cached the role.
Common situations: Failover flipping the Primary; client hitting a member by IP; multi-node deployments without role-aware routing.
Related errors
- Failed to update Secondary node: only a Primary node can upd
- Failed to transfer configuration: only the Primary node can
- Failed to delete Cluster: only a Primary node can delete the
- Failed to add Secondary node: only a Primary node can add a
- Failed to ask Secondary node to leave: only a Primary node c
AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13).
Data as JSON: /api/errors/4871960f9e8f4377.
Report an issue: GitHub.