{"record":{"id":"2705f7ce9b8adfeb","repo":"tailscale/tailscale","slug":"failed-to-remove-finalizer-w","errorCode":null,"errorMessage":"failed to remove finalizer: %w","messagePattern":"failed to remove finalizer: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/k8s-operator/egress-services.go","lineNumber":431,"sourceCode":"\t// Delete the ClusterIP Service and EndpointSlice for the egress\n\t// service.\n\ttypes := []client.Object{\n\t\t&corev1.Service{},\n\t\t&discoveryv1.EndpointSlice{},\n\t}\n\tcrl := egressSvcChildResourceLabels(svc)\n\tfor _, typ := range types {\n\t\tif err := esr.DeleteAllOf(ctx, typ, client.InNamespace(esr.tsNamespace), client.MatchingLabels(crl)); err != nil {\n\t\t\treturn fmt.Errorf(\"error deleting %s: %w\", typ, err)\n\t\t}\n\t}\n\n\tix := slices.Index(svc.Finalizers, FinalizerName)\n\tif ix != -1 {\n\t\tlogger.Debug(\"Removing Tailscale finalizer from Service\")\n\t\tsvc.Finalizers = append(svc.Finalizers[:ix], svc.Finalizers[ix+1:]...)\n\t\tif err := esr.Update(ctx, svc); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to remove finalizer: %w\", err)\n\t\t}\n\t}\n\tesr.mu.Lock()\n\tesr.svcs.Remove(svc.UID)\n\tgaugeEgressServices.Set(int64(esr.svcs.Len()))\n\tesr.mu.Unlock()\n\tlogger.Info(\"successfully cleaned up resources for egress Service\")\n\treturn nil\n}\n\nfunc (esr *egressSvcsReconciler) maybeCleanupProxyGroupConfig(ctx context.Context, svc *corev1.Service, lg *zap.SugaredLogger) error {\n\twantsProxyGroup := svc.Annotations[AnnotationProxyGroup]\n\tcond := tsoperator.GetServiceCondition(svc, tsapi.EgressSvcConfigured)\n\tif cond == nil {\n\t\treturn nil\n\t}\n\tss := strings.Split(cond.Reason, \":\")\n\tif len(ss) < 3 {","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/cmd/k8s-operator/egress-services.go#L413-L449","documentation":"Final step of maybeCleanup: after child resources are deleted, the operator splices FinalizerName out of svc.Finalizers and esr.Update's the Service. Failure here leaves the Service permanently in Terminating (the finalizer is what held deletion open), though the operator keeps retrying the reconcile. This is the classic stuck-finalizer failure mode.","triggerScenarios":"Service Update fails: optimistic-lock conflict with a concurrent writer, RBAC missing services/update, admission webhook rejecting the finalizer removal, or the Service object already gone server-side.","commonSituations":"Namespace deletion churning the same object; GitOps force-applying the Service (including its finalizers) during deletion; webhooks that block updates to terminating objects.","solutions":["Confirm the wrapped cause; conflicts resolve on retry, Forbidden needs RBAC repair.","If the Service is stuck and the operator-managed children (labeled svc/endpointslice in the operator namespace) are already gone, manually clear it: kubectl patch svc <name> -n <ns> -p '{\"metadata\":{\"finalizers\":null}}' --type=merge.","Pause GitOps sync on that Service while it deletes.","Check kubectl get svc -o jsonpath='{.metadata.finalizers}' to confirm removal."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := esr.Update(ctx, svc); err != nil {\n    if apierrors.IsConflict(err) || apierrors.IsNotFound(err) {\n        return nil // requeued or object gone; do not wedge cleanup\n    }\n    return fmt.Errorf(\"failed to remove finalizer: %w\", err)\n}","preventionTips":["Suspend GitOps sync on an egress Service before deleting it so nothing re-adds finalizers mid-teardown.","If operator uninstall is intended and children are gone, clear the finalizer manually: kubectl patch svc <n> -p '{\"metadata\":{\"finalizers\":null}}' --type=merge.","Regularly check for Services stuck in Terminating (kubectl get svc -A | grep Terminating) as a stuck-finalizer canary."],"tags":["kubernetes","tailscale-operator","finalizer","stuck-deletion","service-deletion"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}