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

  1. Inspect the labels: kubectl get mgmtcluster <name> -o jsonpath='{.metadata.labels}'
  2. Set the missing counterpart label if the cluster really is CAPI-native, or remove both to mark it imported — whichever reflects reality
  3. 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

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


AI-assisted analysis of rancher/rancher@932558d4e6 (2026-08-16). Data as JSON: /api/errors/54933f5a9e18c4f6. Report an issue: GitHub.