tailscale/tailscale · error
failed to add finalizer to PeerRelay %q: %w
Error message
failed to add finalizer to PeerRelay %q: %w
What it means
Adding the reconciler's finalizer to the PeerRelay object failed, meaning deletion cleanup (devices, Secrets, Services) can't be guaranteed before removal; the create/update path aborts and reconciliation requeues.
Source
Thrown at k8s-operator/reconciler/peerrelay/peerrelay.go:214
if !pr.DeletionTimestamp.IsZero() {
return r.delete(ctx, logger, &pr)
}
return r.createOrUpdate(ctx, logger, &pr)
}
func (r *Reconciler) reportTailnetUnavailable(ctx context.Context, logger *zap.SugaredLogger, pr *tsapi.PeerRelay, tsErr error) (reconcile.Result, error) {
operatorutils.SetPeerRelayCondition(pr, tsapi.PeerRelayReady, metav1.ConditionFalse, ReasonTailnetUnavailable, tsErr.Error(), r.clock, logger)
if err := r.Status().Update(ctx, pr); err != nil {
return reconcile.Result{}, errors.Join(tsErr, fmt.Errorf("failed to update PeerRelay status: %w", err))
}
return reconcile.Result{}, tsErr
}
func (r *Reconciler) createOrUpdate(ctx context.Context, logger *zap.SugaredLogger, pr *tsapi.PeerRelay) (reconcile.Result, error) {
if err := reconciler.EnsureFinalizer(ctx, r.Client, pr, reconciler.Finalizer); err != nil {
return reconcile.Result{}, fmt.Errorf("failed to add finalizer to PeerRelay %q: %w", pr.Name, err)
}
r.tracker.Add(pr.UID)
replicas := int32(1)
if pr.Spec.Replicas != nil {
replicas = *pr.Spec.Replicas
}
r.reissuer.EnsureState(pr.Name, int(replicas))
// Belt-and-braces: CEL on the CRD enforces this at admission, but we also validate here to guard against older
// clusters without CEL, resources created before the CRD schema landed, or hand-edited status paths. If the user
// hasn't supplied enough EIPs for the requested replica count we refuse to touch existing state and surface the
// condition so they can fix the spec.
if pr.Spec.AWS != nil && int32(len(pr.Spec.AWS.ElasticIPs)) < replicas {
message := fmt.Sprintf("spec.aws.elasticIPs has %d entries but spec.replicas is %d", len(pr.Spec.AWS.ElasticIPs), replicas)
operatorutils.SetPeerRelayCondition(pr, tsapi.PeerRelayReady, metav1.ConditionFalse, ReasonAWSConfigInvalid, message, r.clock, logger)View on GitHub (pinned to 6e0912f979)
Solutions
- Adding the finalizer to the PeerRelay failed; check RBAC update permission on PeerRelays and retry.
Defensive patterns
Strategy: retry
When it happens
Trigger: Thrown at k8s-operator/reconciler/peerrelay/peerrelay.go:214 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18).
Data as JSON: /api/errors/114141dac0de0af9.
Report an issue: GitHub.