tailscale/tailscale · error
failed to get ing: %w
Error message
failed to get ing: %w
What it means
The Ingress reconciler's initial Get of the Ingress object failed with an error other than NotFound (which is handled and treated as deletion). This is a straight Kubernetes API read failure — RBAC denial on ingresses, apiserver unreachability, or request timeout — and aborts the reconcile before any provisioning logic runs.
Source
Thrown at cmd/k8s-operator/ingress.go:76
var (
// gaugeIngressResources tracks the number of ingress resources that we're
// currently managing.
gaugeIngressResources = clientmetric.NewGauge(kubetypes.MetricIngressResourceCount)
)
func (a *IngressReconciler) Reconcile(ctx context.Context, req reconcile.Request) (_ reconcile.Result, err error) {
logger := a.logger.With("Ingress", req.NamespacedName)
logger.Debugf("starting reconcile")
defer logger.Debugf("reconcile finished")
ing := new(networkingv1.Ingress)
err = a.Get(ctx, req.NamespacedName, ing)
if apierrors.IsNotFound(err) {
// Request object not found, could have been deleted after reconcile request.
logger.Debugf("ingress not found, assuming it was deleted")
return reconcile.Result{}, nil
} else if err != nil {
return reconcile.Result{}, fmt.Errorf("failed to get ing: %w", err)
}
if !ing.DeletionTimestamp.IsZero() || !a.shouldExpose(ing) {
// TODO(irbekrm): this message is confusing if the Ingress is an HA Ingress
logger.Debugf("ingress is being deleted or should not be exposed, cleaning up")
return reconcile.Result{}, a.maybeCleanup(ctx, logger, ing)
}
if err := a.maybeProvision(ctx, logger, ing); err != nil {
if strings.Contains(err.Error(), optimisticLockErrorMsg) {
logger.Infof("optimistic lock error, retrying: %s", err)
} else {
return reconcile.Result{}, err
}
}
return reconcile.Result{}, nil
}
View on GitHub (pinned to cfe32b8be6)
Solutions
- Check operator RBAC: kubectl auth can-i get ingresses --as=system:serviceaccount:<ns>:tailscale-operator
- For transient errors, rely on controller-runtime's exponential backoff requeue
- Verify apiserver health and operator-to-control-plane networking
- Ensure the operator's ingress class filter matches a class it can watch
Defensive patterns
Strategy: retry
Try / catch
err = a.Get(ctx, req.NamespacedName, ing)
if apierrors.IsNotFound(err) {
return reconcile.Result{}, nil // deleted, nothing to do
} else if err != nil {
return reconcile.Result{}, fmt.Errorf("failed to get ing: %w", err) // controller-runtime requeues
} Prevention
- Install the operator with full bundled RBAC (ingresses get/list/watch)
- Only watch namespaces the operator is granted access to
- Monitor controller workqueue error rate to catch API read failures early
When it happens
Trigger: a.Get(ctx, req.NamespacedName, ing) at the top of IngressReconciler.Reconcile. Fires when the operator service account cannot get ingresses in the watched namespace or the control plane call fails transiently.
Common situations: Operator installed without Ingress read permissions; watching a namespace it has no access to; apiserver restart; stale watch causing a re-list that errors.
Related errors
- error syncing ingress service config: %w
- ingress proxy: error retrieving current status: %w
- ingress proxy: error setting status: %w
- storing device ID in Kubernetes Secret: %w
- storing device IPs and FQDN in Kubernetes Secret: %w
AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15).
Data as JSON: /api/errors/00c69f3148ec08b5.
Report an issue: GitHub.