tailscale/tailscale · error
failed to get EndpointSlice: %w
Error message
failed to get EndpointSlice: %w
What it means
Thrown by the egress EndpointSlice reconciler when controller-runtime's Get of the EndpointSlice fails with an error other than NotFound (NotFound is handled one line above and returns cleanly). Any error reaching this line is infrastructure-level: API server connectivity problems, RBAC denying discovery.k8s.io EndpointSlices get, throttling, or a canceled context.
Source
Thrown at cmd/k8s-operator/egress-eps.go:50
}
// Reconcile reconciles an EndpointSlice for a tailnet service. It updates the EndpointSlice with the endpoints of
// those ProxyGroup Pods that are ready to route traffic to the tailnet service.
// It compares tailnet service state stored in egress proxy state Secrets by containerboot with the desired
// configuration stored in proxy-cfg ConfigMap to determine if the endpoint is ready.
func (er *egressEpsReconciler) Reconcile(ctx context.Context, req reconcile.Request) (res reconcile.Result, err error) {
lg := er.logger.With("Service", req.NamespacedName)
lg.Debugf("starting reconcile")
defer lg.Debugf("reconcile finished")
eps := new(discoveryv1.EndpointSlice)
err = er.Get(ctx, req.NamespacedName, eps)
if apierrors.IsNotFound(err) {
lg.Debugf("EndpointSlice not found")
return reconcile.Result{}, nil
}
if err != nil {
return reconcile.Result{}, fmt.Errorf("failed to get EndpointSlice: %w", err)
}
if !eps.DeletionTimestamp.IsZero() {
lg.Debugf("EnpointSlice is being deleted")
return res, nil
}
// Get the user-created ExternalName Service and use its status conditions to determine whether cluster
// resources are set up for this tailnet service.
svc := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: eps.Labels[LabelParentName],
Namespace: eps.Labels[LabelParentNamespace],
},
}
err = er.Get(ctx, client.ObjectKeyFromObject(svc), svc)
if apierrors.IsNotFound(err) {
lg.Infof("ExternalName Service %s/%s not found, perhaps it was deleted", svc.Namespace, svc.Name)
return res, nilView on GitHub (pinned to cfe32b8be6)
Solutions
- Verify the operator ClusterRole includes verbs=[get,list,watch] on discovery.k8s.io/endpointslices
- Check apiserver health and client-side rate limiting (client-side QPS/burst settings)
- Do nothing special for single occurrences — returning the error makes controller-runtime requeue with exponential backoff
- If persistent, inspect operator logs for the wrapped error text and fix that root cause
Defensive patterns
Strategy: try-catch
Type guard
// Distinguish handled not-found from real infrastructure errors
func isInfraErr(err error) bool {
return err != nil && !apierrors.IsNotFound(err)
} Try / catch
// Go: return the error; controller-runtime requeues with exponential backoff
err = er.Get(ctx, req.NamespacedName, eps)
if apierrors.IsNotFound(err) {
return reconcile.Result{}, nil
}
if err != nil {
return reconcile.Result{}, fmt.Errorf("failed to get EndpointSlice: %w", err)
} Prevention
- Keep the ClusterRole for the operator in sync with every new watched type
- Never swallow non-NotFound Get errors — returning them is what powers backoff requeue
- Alert on elevated reconcile error rates rather than individual transient failures
When it happens
Trigger: The operator's service account lacks get on endpointslices (RBAC regression); API server temporarily unreachable or overloaded (429/503); watch/cache resync races surfacing as transient read failures; request context canceled during reconciliation.
Common situations: Deploying a new operator version without the updated ClusterRole; apiserver disruption causing reconcile storms; large clusters where list/watch pressure produces throttled reads.
Related errors
- error retrieving ExternalName Service: %w
- error retrieving tailnet services configuration: %w
- error listing Pods for ProxyGroup %s: %w
- failed to get Service: %w
- failed to remove finalizer %q: %w
AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15).
Data as JSON: /api/errors/3ab384ec0ad5c6fe.
Report an issue: GitHub.