rancher/rancher · error
mgmt cluster %s carries only one of %s/%s; both must be set
Error message
mgmt cluster %s carries only one of %s/%s; both must be set for a CAPI-native cluster
What it means
identity.go classifies a management cluster using two labels (capr.CAPIClusterOwnerLabel and capr.CAPIClusterOwnerNSLabel): both set means CAPI-native, both absent means imported. Exactly one set is treated as misconfiguration and rejected rather than silently defaulting to 'imported' — an explicit improvement over the prior regex-based failure mode. The error names the cluster and both label keys.
Source
Thrown at pkg/capr/configserver/identity.go:104
// navigate mgmt shell → provv1.Cluster → CAPI cluster in fleet-default. Classify as
// KindV2Prov so onSecretChange falls through to that path rather than mistakenly running the
// imported RKE2/K3s (mgmt v3 Node) handler.
if mgmtCluster.Annotations["provisioning.cattle.io/administrated"] == "true" {
return &LifecycleContext{
Kind: KindV2Prov,
TargetNamespace: tokenNamespace,
MgmtCluster: mgmtCluster,
}, nil
}
ownerName := mgmtCluster.Labels[capr.CAPIClusterOwnerLabel]
ownerNS := mgmtCluster.Labels[capr.CAPIClusterOwnerNSLabel]
// Both labels must be set together — mixed state is misconfiguration and we refuse to
// silently default to "imported", which has historically been the case with the prior
// regex-based failure mode.
if (ownerName == "") != (ownerNS == "") {
return nil, fmt.Errorf(
"mgmt cluster %s carries only one of %s/%s; both must be set for a CAPI-native cluster",
mgmtCluster.Name, capr.CAPIClusterOwnerLabel, capr.CAPIClusterOwnerNSLabel)
}
if ownerName == "" {
return &LifecycleContext{
Kind: KindImported,
TargetNamespace: mgmtCluster.Name,
MgmtCluster: mgmtCluster,
}, nil
}
capiCluster, err := capiClusterCache.Get(ownerNS, ownerName)
if apierrors.IsNotFound(err) {
return nil, fmt.Errorf(
"mgmt cluster %s references CAPI cluster %s/%s, but that cluster was not found",
mgmtCluster.Name, ownerNS, ownerName)
}View on GitHub (pinned to 932558d4e6)
Solutions
- Inspect the labels: kubectl get mgmtcluster <name> -o jsonpath='{.metadata.labels}'
- Set the missing counterpart label if the cluster really is CAPI-native, or remove both to mark it imported — whichever reflects reality
- Retry the machine config request once the pair is consistent
Example fix
# half-labelled (broken) -> consistent CAPI-native kubectl label mgmtcluster <name> cluster-api.cattle.io/cluster-owner=<capiClusterName> kubectl label mgmtcluster <name> cluster-api.cattle.io/cluster-owner-ns=<capiClusterNamespace> # or mark imported by removing both kubectl label mgmtcluster <name> cluster-api.cattle.io/cluster-owner- cluster-api.cattle.io/cluster-owner-ns-
Defensive patterns
Strategy: validation
Validate before calling
ownerName := mgmt.Labels[capr.CAPIClusterOwnerLabel]
ownerNS := mgmt.Labels[capr.CAPIClusterOwnerNSLabel]
if (ownerName == "") != (ownerNS == "") {
return fmt.Errorf("refusing request: mgmt cluster %s has half-set owner labels; set both or neither", mgmt.Name)
} Type guard
func ownerLabelsConsistent(l map[string]string) bool {
name, ns := l[capr.CAPIClusterOwnerLabel], l[capr.CAPIClusterOwnerNSLabel]
return (name == "" && ns == "") || (name != "" && ns != "")
} Try / catch
lc, err := getLifecycleContext(mgmt)
if err != nil && strings.Contains(err.Error(), "both must be set") {
// repair labels to a consistent state, then retry the request
if repairErr := fixOwnerLabels(mgmt); repairErr != nil {
return repairErr
}
lc, err = getLifecycleContext(mgmt)
}
if err != nil {
return err
} Prevention
- Never label mgmt clusters by hand — let provisioning write both owner keys atomically
- Lint restored objects for owner-label pairs being both-set or both-absent
- Include this check in upgrade validation jobs that migrate old regex-based classifications
When it happens
Trigger: Any configserver request (machine config fetch) for an mgmt cluster whose owner labels are half-written: manual kubectl label of only one key, a migration that crashed between the two writes, or a partial restore.
Common situations: Operators editing labels by hand; upgrade/migration jobs interrupted mid-label; backup restore reapplying only some labels.
Related errors
- unable to find machine %s, found %d machine(s)
- unable to find cluster name for machine
- timeout waiting for %s/%s to be ready
- mgmt cluster %s references CAPI cluster %s/%s, but that clus
- error deleting machine [%s], machine must be deleted manuall
AI-assisted analysis of rancher/rancher@932558d4e6 (2026-08-16).
Data as JSON: /api/errors/54933f5a9e18c4f6.
Report an issue: GitHub.