tailscale/tailscale · error
failed to list config Secrets: %w
Error message
failed to list config Secrets: %w
What it means
Thrown by maybeAdvertiseServices when the controller-runtime client's List call fails while fetching all ProxyGroup config Secrets (label selector built by pgSecretLabels(pg.Name, kubetypes.LabelSecretTypeConfig)) in the operator namespace. The reconciler needs these Secrets to patch the advertise-services list in each proxy's config. The %w is the API server's List error.
Source
Thrown at cmd/k8s-operator/api-server-proxy-pg.go:346
}); err != nil {
return fmt.Errorf("failed to create or update Role %s: %w", role.Name, err)
}
rolebinding := certSecretRoleBinding(pg, r.tsNamespace, domain)
if _, err := createOrUpdate(ctx, r.Client, r.tsNamespace, rolebinding, func(rb *rbacv1.RoleBinding) {
rb.Labels = rolebinding.Labels
rb.Subjects = rolebinding.Subjects
rb.RoleRef = rolebinding.RoleRef
}); err != nil {
return fmt.Errorf("failed to create or update RoleBinding %s: %w", rolebinding.Name, err)
}
return nil
}
func (r *KubeAPIServerTSServiceReconciler) maybeAdvertiseServices(ctx context.Context, pg *tsapi.ProxyGroup, serviceName tailcfg.ServiceName, logger *zap.SugaredLogger) error {
// Get all config Secrets for this ProxyGroup
cfgSecrets := &corev1.SecretList{}
if err := r.List(ctx, cfgSecrets, client.InNamespace(r.tsNamespace), client.MatchingLabels(pgSecretLabels(pg.Name, kubetypes.LabelSecretTypeConfig))); err != nil {
return fmt.Errorf("failed to list config Secrets: %w", err)
}
// Only advertise a Tailscale Service once the TLS certs required for
// serving it are available.
shouldBeAdvertised, err := hasCerts(ctx, r.Client, r.tsNamespace, serviceName, pg)
if err != nil {
return fmt.Errorf("error checking TLS credentials provisioned for Tailscale Service %q: %w", serviceName, err)
}
var advertiseServices []string
if shouldBeAdvertised {
advertiseServices = []string{serviceName.String()}
}
for _, s := range cfgSecrets.Items {
if len(s.Data[kubetypes.KubeAPIServerConfigFile]) == 0 {
continue
}
View on GitHub (pinned to cfe32b8be6)
Solutions
- Check the wrapped error verb/URL; test with kubectl auth can-i list secrets -n <operator-namespace> --as=system:serviceaccount:<ns>:operator.
- Ensure the operator's namespaced Role in its installation namespace includes verbs [list, get, watch] on secrets.
- Confirm the operator Pod's ACTAILSCALE_OPERATOR_NAMESPACE / install namespace matches where the ProxyGroup resources are created by the operator.
- Retry after API server recovery; the reconciler requeues on error automatically.
- Look for network policies blocking the operator Pod's egress to the Kubernetes API.
Example fix
# before: Role allows only reading named secrets - apiGroups: [""] resources: ["secrets"] resourceNames: ["proxygroup-foo-0"] verbs: ["get"] # after: allow listing operator-managed config secrets - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "watch", "create", "update", "patch"]
Defensive patterns
Strategy: retry
Validate before calling
// kubectl auth can-i list secrets -n <operator-ns> \ // --as=system:serviceaccount:<operator-ns>:operator // expected output: yes
Type guard
func isListDenied(err error) bool {
var status *apierrors.StatusError
if errors.As(err, &status) {
return status.ErrStatus.Reason == metav1.StatusReasonForbidden
}
return false
} Try / catch
if err := r.List(ctx, cfgSecrets, client.InNamespace(r.tsNamespace), client.MatchingLabels(sel)); err != nil {
if isListDenied(err) {
logger.Error("cannot list config Secrets — fix operator Role (verbs: list,watch on secrets)")
}
return ctrl.Result{}, err // transient API errors retried with backoff
} Prevention
- Keep list+watch on secrets in the operator's namespaced Role even under least-privilege reviews.
- Pin the operator install namespace and never move it without re-applying RBAC.
- Label all operator-owned Secrets and never rely on external writers for them.
- Monitor operator events; repeated list failures indicate RBAC drift before user-facing breakage.
When it happens
Trigger: r.List(ctx, cfgSecrets, client.InNamespace(r.tsNamespace), client.MatchingLabels(...)) fails when the operator ServiceAccount cannot list secrets with that label selector in the namespace, the namespace was deleted/renamed (operator installed with a different --namespace than its RBAC covers), or the API server errors on the watch-backed list.
Common situations: Operator deployed to a non-default namespace but its Role granting secrets list only exists in the old namespace; least-privilege RBAC tuned down to get-only (no list verb); label-selector optimizations requiring the secrets resource name in RBAC (list secrets with fieldSelector/labelSelector needs 'list' on 'secrets'); transient API server or etcd issues.
Related errors
- failed to create or update Role %s: %w
- failed to create or update RoleBinding %s: %w
- error checking TLS credentials provisioned for Tailscale Ser
- error loading config from Secret %q: %w
- config Secret %q does not contain APIServerProxy config
AI-assisted analysis of tailscale/tailscale@cfe32b8be6 (2026-08-15).
Data as JSON: /api/errors/14aa1813abc89fa7.
Report an issue: GitHub.