temporalio/temporal · critical
<dynamic updateVersionToClusterName err>
Error message
<dynamic updateVersionToClusterName err>
What it means
After basic validation, NewMetadata calls updateVersionToClusterName, which builds a failover-version-to-cluster-name map from the supplied clusterInfo. If that helper returns an error (e.g. overlapping initial failover versions or duplicate version assignments across clusters), the constructor panics with the error text. This ensures ambiguous failover version mappings can never reach the running server.
Source
Thrown at common/cluster/metadata.go:158
clusterInfo map[string]ClusterInformation,
clusterMetadataStore persistence.ClusterMetadataManager,
refreshDuration dynamicconfig.DurationPropertyFn,
logger log.Logger,
) Metadata {
if len(clusterInfo) == 0 {
panic("Empty cluster information")
} else if len(masterClusterName) == 0 {
panic("Master cluster name is empty")
} else if len(currentClusterName) == 0 {
panic("Current cluster name is empty")
} else if failoverVersionIncrement == 0 || failoverVersionIncrement > math.MaxInt32 {
panic("Version increment <= 0 or > 2147483647")
}
versionToClusterName, err := updateVersionToClusterName(clusterInfo, failoverVersionIncrement)
if err != nil {
// nolint:forbidigo // matches the other startup-config panics in this constructor
panic(err.Error())
}
if _, ok := clusterInfo[currentClusterName]; !ok {
panic("Current cluster is not specified in cluster info")
}
if _, ok := clusterInfo[masterClusterName]; !ok {
panic("Master cluster is not specified in cluster info")
}
copyClusterInfo := make(map[string]ClusterInformation)
maps.Copy(copyClusterInfo, clusterInfo)
if refreshDuration == nil {
refreshDuration = dynamicconfig.GetDurationPropertyFn(refreshInterval)
}
return &metadataImpl{
status: common.DaemonStatusInitialized,
enableGlobalNamespace: enableGlobalNamespace,
failoverVersionIncrement: failoverVersionIncrement,
masterClusterName: masterClusterName,View on GitHub (pinned to bde624efd1)
Solutions
- Assign each cluster in clusterInfo a unique InitialFailoverVersion that is consistent with the failoverVersionIncrement
- Log/inspect the panicked error text to identify which cluster/version conflicted
- Validate cluster info with updateVersionToClusterName in a preflight check before restarting the service
Example fix
// before
clusterInfo := map[string]cluster.ClusterInformation{
"clusterA": {InitialFailoverVersion: 1},
"clusterB": {InitialFailoverVersion: 1},
}
// after
clusterInfo := map[string]cluster.ClusterInformation{
"clusterA": {InitialFailoverVersion: 1},
"clusterB": {InitialFailoverVersion: 100000000},
} Defensive patterns
Strategy: validation
Validate before calling
seen := map[int64]string{}
for name, info := range clusterInfo {
if other, dup := seen[info.InitialFailoverVersion]; dup {
return fmt.Errorf("clusters %s and %s share InitialFailoverVersion %d", other, name, info.InitialFailoverVersion)
}
seen[info.InitialFailoverVersion] = name
} Prevention
- Space InitialFailoverVersion values by the failoverVersionIncrement
- Lint/generate multi-cluster configs from one source of truth
- Run metadata construction in a preflight startup step that logs panics clearly
When it happens
Trigger: cluster.NewMetadata with a clusterInfo map where two clusters share the same InitialFailoverVersion (producing a collision), or otherwise inconsistent cluster info that updateVersionToClusterName rejects.
Common situations: Hand-editing multi-cluster configs and giving two remote clusters the same initial failover version; programmatically generating cluster info where versions are not spaced by failoverVersionIncrement.
Related errors
- Version increment <= 0 or > 2147483647
- Current cluster name is empty
- Current cluster is not specified in cluster info
- Master cluster is not specified in cluster info
- ClusterMetadata encountered local namesapce with failover ve
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/cfd36cc0d142a093.
Report an issue: GitHub.