cilium/cilium · error

refusing to overwrite synced Secret %s/%s without ownership

Error message

refusing to overwrite synced Secret %s/%s without ownership labels

What it means

ensureOwnedBy also validates the existing (target) Secret: if it lacks ownership labels, the sync refuses to overwrite it, because overwriting an unlabeled secret could clobber a secret not managed by the sync (or one whose ownership history was lost). This is a safety guard against hijacking pre-existing secrets.

Source

Thrown at operator/pkg/secretsync/ownership.go:51

	}

	return types.NamespacedName{Namespace: namespace, Name: name}, true
}

func isOwnedBy(obj client.Object, owner types.NamespacedName, namespaceLabel, nameLabel string) bool {
	existingOwner, ok := ownerFromLabels(obj, namespaceLabel, nameLabel)
	return ok && existingOwner == owner
}

func ensureOwnedBy(existing, desired client.Object, namespaceLabel, nameLabel string) error {
	desiredOwner, ok := ownerFromLabels(desired, namespaceLabel, nameLabel)
	if !ok {
		return fmt.Errorf("desired synced Secret %s/%s is missing ownership labels", desired.GetNamespace(), desired.GetName())
	}

	existingOwner, ok := ownerFromLabels(existing, namespaceLabel, nameLabel)
	if !ok {
		return fmt.Errorf("refusing to overwrite synced Secret %s/%s without ownership labels", existing.GetNamespace(), existing.GetName())
	}
	if existingOwner != desiredOwner {
		return fmt.Errorf("refusing to overwrite synced Secret %s/%s owned by %s/%s with data from %s/%s", existing.GetNamespace(), existing.GetName(), existingOwner.Namespace, existingOwner.Name, desiredOwner.Namespace, desiredOwner.Name)
	}

	return nil
}

func setSourceAnnotations(obj client.Object, kind string, source types.NamespacedName) {
	annotations := make(map[string]string, len(obj.GetAnnotations())+3)
	maps.Copy(annotations, obj.GetAnnotations())

	annotations[SourceKindAnnotation] = kind
	annotations[SourceNamespaceAnnotation] = source.Namespace
	annotations[SourceNameAnnotation] = source.Name

	obj.SetAnnotations(annotations)
}

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Delete the unlabeled conflicting Secret and let the sync recreate it with proper ownership labels
  2. Re-add the correct Cilium ownership labels to the existing Secret
  3. Rename your pre-existing Secret or the synced Secret to avoid the collision
  4. Verify ownership label keys match the current Cilium version's expectations

Example fix

# before
kubectl create secret generic my-secret ... # collides with synced name
# after
kubectl delete secret my-secret -n ns && kubectl annotate secret ... # or rename to my-secret-manual
Defensive patterns

Strategy: validation

Validate before calling

existing := &corev1.Secret{}
err := cl.Get(ctx, key, existing)
if err == nil && !isOwnershipLabeled(existing, namespaceLabel, nameLabel) {
    return fmt.Errorf("conflict: secret %s exists without cilium ownership labels; rename or delete it first", key)
}

Type guard

func isOwnershipLabeled(o client.Object, nsLabel, nameLabel string) bool {
    l := o.GetLabels()
    _, ns := l[nsLabel]; _, n := l[nameLabel]
    return ns && n
}

Try / catch

if err := ensureSyncedSecret(ctx, cl, reg, key); err != nil {
    if strings.Contains(err.Error(), "without ownership labels") {
        // delete the unlabeled secret (after confirming it is not user-managed), then retry
    }
    return err
}

Prevention

When it happens

Trigger: ensureSyncedSecret finds an existing Secret in the cluster whose labels do not contain the ownership keys, and attempts to overwrite it with desired content.

Common situations: A same-named Secret pre-exists (created manually or by another controller) before cilium's sync runs; labels were stripped by another tool; or an upgrade changed the label keys so old secrets appear unlabeled.

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/b3e4dc02588526e5. Report an issue: GitHub.