tailscale/tailscale · warning

error deleting Role for domain name %s: %w

Error message

error deleting Role for domain name %s: %w

What it means

During cert cleanup, cl.DeleteAllOf for Roles labelled with the ProxyGroup name and domain failed — the Role half of the cert RBAC pair. Identical failure modes to the RoleBinding deletion: RBAC denial or apiserver error. Cleanup stops before the Secret deletion step, so resources leak until a successful retry.

Source

Thrown at cmd/k8s-operator/ingress-for-pg.go:1009

	}); err != nil {
		return fmt.Errorf("failed to create or update RoleBinding %s: %w", rolebinding.Name, err)
	}
	return nil
}

// cleanupCertResources ensures that the TLS Secret and associated RBAC
// resources that allow proxies to read/write to the Secret are deleted.
func cleanupCertResources(ctx context.Context, cl client.Client, tsNamespace string, serviceName tailcfg.ServiceName, pg *tsapi.ProxyGroup) error {
	domainName, err := dnsNameForService(ctx, cl, serviceName, pg, tsNamespace)
	if err != nil {
		return fmt.Errorf("error getting DNS name for Tailscale Service %s: %w", serviceName, err)
	}
	labels := certResourceLabels(pg.Name, domainName)
	if err := cl.DeleteAllOf(ctx, &rbacv1.RoleBinding{}, client.InNamespace(tsNamespace), client.MatchingLabels(labels)); err != nil {
		return fmt.Errorf("error deleting RoleBinding for domain name %s: %w", domainName, err)
	}
	if err := cl.DeleteAllOf(ctx, &rbacv1.Role{}, client.InNamespace(tsNamespace), client.MatchingLabels(labels)); err != nil {
		return fmt.Errorf("error deleting Role for domain name %s: %w", domainName, err)
	}
	if err := cl.DeleteAllOf(ctx, &corev1.Secret{}, client.InNamespace(tsNamespace), client.MatchingLabels(labels)); err != nil {
		return fmt.Errorf("error deleting Secret for domain name %s: %w", domainName, err)
	}
	return nil
}

// requeueInterval returns a time duration between 5 and 10 minutes, which is
// the period of time after which an HA Ingress, whose Tailscale Service has been newly
// created or changed, needs to be requeued. This is to protect against
// Tailscale Service's owner references being overwritten as a result of concurrent
// updates during multi-clutster Ingress create/update operations.
func requeueInterval() time.Duration {
	return time.Duration(rand.N(5)+5) * time.Minute
}

// certSecretRole creates a Role that will allow proxies to manage the TLS
// Secret for the given domain. Domain must be a valid Kubernetes resource name.

View on GitHub (pinned to cfe32b8be6)

Solutions

  1. Verify operator can delete roles in its namespace
  2. Let the reconciler retry — DeleteAllOf is idempotent
  3. Manually delete leftovers: kubectl delete role -n <operator-ns> -l tailscale.com/parent-proxy-group=<pgName>
Defensive patterns

Strategy: retry

Try / catch

if err := cl.DeleteAllOf(ctx, &rbacv1.Role{}, client.InNamespace(ns), client.MatchingLabels(labels)); err != nil {
    if apierrors.IsNotFound(err) {
        return nil
    }
    return fmt.Errorf("error deleting Role for domain name %s: %w", domainName, err)
}

Prevention

When it happens

Trigger: cl.DeleteAllOf(ctx, &rbacv1.Role{}, client.InNamespace(tsNamespace), client.MatchingLabels(labels)) after the RoleBinding delete in cleanupCertResources. Fires on missing delete on roles or control-plane errors.

Common situations: Narrowed ClusterRole without role delete; admission webhooks blocking collection deletes; overloaded apiserver during cluster teardown.

Related errors


AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15). Data as JSON: /api/errors/6c034d96c2b3cf78. Report an issue: GitHub.