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

  1. Check operator RBAC: kubectl auth can-i get ingresses --as=system:serviceaccount:<ns>:tailscale-operator
  2. For transient errors, rely on controller-runtime's exponential backoff requeue
  3. Verify apiserver health and operator-to-control-plane networking
  4. 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

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


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