{"record":{"id":"485eb61fd338385b","repo":"kubernetes/kops","slug":"cluster-ownerref-not-set-on-capi-cluster-v-v","errorCode":null,"errorMessage":"cluster ownerRef not set on CAPI cluster %v/%v","messagePattern":"cluster ownerRef not set on CAPI cluster (.+?)/(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/controllers/clusterapi/utils.go","lineNumber":99,"sourceCode":"\t}\n\n\treturn capiCluster, nil\n}\n\nfunc getKopsClusterFromCAPICluster(ctx context.Context, kube client.Client, capiCluster *unstructured.Unstructured) (*kopsapi.Cluster, error) {\n\tvar clusterKey types.NamespacedName\n\n\tfor _, ownerRef := range capiCluster.GetOwnerReferences() {\n\t\tif ownerRef.Kind == \"Cluster\" && strings.HasPrefix(ownerRef.APIVersion, \"kops.k8s.io/\") {\n\t\t\tclusterKey = types.NamespacedName{\n\t\t\t\tNamespace: capiCluster.GetNamespace(),\n\t\t\t\tName:      ownerRef.Name,\n\t\t\t}\n\t\t}\n\t}\n\n\tif clusterKey.Name == \"\" {\n\t\treturn nil, fmt.Errorf(\"cluster ownerRef not set on CAPI cluster %v/%v\", capiCluster.GetNamespace(), capiCluster.GetName())\n\t}\n\n\tcluster := &kopsapi.Cluster{}\n\tif err := kube.Get(ctx, clusterKey, cluster); err != nil {\n\t\treturn nil, fmt.Errorf(\"error fetching cluster %v: %w\", clusterKey, err)\n\t}\n\n\treturn cluster, nil\n}\n\nfunc getKopsControlPlaneFromCAPICluster(ctx context.Context, kube client.Client, capiCluster *unstructured.Unstructured) (*v1beta1.KopsControlPlane, error) {\n\tcapiClusterKey := types.NamespacedName{\n\t\tNamespace: capiCluster.GetNamespace(),\n\t\tName:      capiCluster.GetName(),\n\t}\n\tname, _, _ := unstructured.NestedString(capiCluster.Object, \"spec\", \"controlPlaneRef\", \"name\")\n\tif name == \"\" {\n\t\treturn nil, fmt.Errorf(\"controlPlaneRef.name not set for %v\", capiClusterKey)","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/pkg/controllers/clusterapi/utils.go#L81-L117","documentation":"getKopsClusterFromCAPICluster finds the kops Cluster that owns a CAPI Cluster by scanning capiCluster's ownerReferences for one with Kind \"Cluster\" and APIVersion prefixed \"kops.k8s.io/\". This error is thrown when no such owner reference exists, meaning the CAPI Cluster is not linked to any kops.k8s.io Cluster and the kops Cluster name cannot be derived.","triggerScenarios":"Reconcile -> getKopsClusterFromCAPICluster where the CAPI Cluster object has an empty ownerReferences list, or owner references exist but none matches Kind=Cluster with APIVersion kops.k8s.io/* (e.g. owner is another kind, or a different API group).","commonSituations":"CAPI Cluster created manually instead of by the kops control-plane controller that normally sets the kops ownerRef; ownerRef stripped by copy/edit of the manifest; wrong owner kind or API version (e.g. kops.k8s.io vs another group) after a version migration.","solutions":["Ensure the CAPI Cluster is created by the kops CAPI machinery so it sets the kops.k8s.io Cluster ownerRef; delete and recreate through the normal flow","Manually add the correct ownerReference (apiVersion: kops.k8s.io/v1alpha2, kind: Cluster, name: <kops-cluster>) to the CAPI Cluster","Verify the kops Cluster exists in the same namespace and its name matches the ownerRef","Check for API version changes across kops upgrades that break the strings.HasPrefix(ownerRef.APIVersion, \"kops.k8s.io/\") match"],"exampleFix":"# before: CAPI Cluster without kops ownerRef\nmetadata:\n  name: my-capi-cluster\n  ownerReferences: []\n# after\nmetadata:\n  name: my-capi-cluster\n  ownerReferences:\n  - apiVersion: kops.k8s.io/v1alpha2\n    kind: Cluster\n    name: my-kops-cluster\n    uid: <uid>\n    controller: true","handlingStrategy":"validation","validationCode":"func hasKopsOwnerRef(capiCluster *unstructured.Unstructured) bool {\n\tfor _, ref := range capiCluster.GetOwnerReferences() {\n\t\tif ref.Kind == \"Cluster\" && strings.HasPrefix(ref.APIVersion, \"kops.k8s.io/\") {\n\t\t\treturn ref.Name != \"\"\n\t\t}\n\t}\n\treturn false\n}\n// skip reconciliation until hasKopsOwnerRef(capiCluster) is true","typeGuard":"func kopsOwnerClusterKey(capiCluster *unstructured.Unstructured) (types.NamespacedName, bool) {\n\tfor _, ref := range capiCluster.GetOwnerReferences() {\n\t\tif ref.Kind == \"Cluster\" && strings.HasPrefix(ref.APIVersion, \"kops.k8s.io/\") && ref.Name != \"\" {\n\t\t\treturn types.NamespacedName{Namespace: capiCluster.GetNamespace(), Name: ref.Name}, true\n\t\t}\n\t}\n\treturn types.NamespacedName{}, false\n}","tryCatchPattern":null,"preventionTips":["Never create cluster.x-k8s.io Cluster objects manually; let the kops CAPI controller create them with the ownerRef","Preserve ownerReferences when exporting/importing or editing Cluster manifests","After kops upgrades, verify the ownerRef APIVersion still matches kops.k8s.io/*","Use admission policies to reject CAPI Clusters lacking a kops.k8s.io ownerRef"],"tags":["kubernetes","cluster-api","owner-references","kops"],"backgroundTag":"missing-owner-reference","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}