{"record":{"id":"dfb04cbfc8877bfd","repo":"tailscale/tailscale","slug":"error-creating-tailscale-service-w-dfb04c","errorCode":null,"errorMessage":"error creating Tailscale Service: %w","messagePattern":"error creating Tailscale Service: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/k8s-operator/ingress-for-pg.go","lineNumber":363,"sourceCode":"\t\tName:        serviceName.String(),\n\t\tTags:        tags,\n\t\tPorts:       tsSvcPorts,\n\t\tComment:     managedTSServiceComment,\n\t\tAnnotations: updatedAnnotations,\n\t}\n\tif existingTSSvc != nil {\n\t\ttsSvc.Addrs = existingTSSvc.Addrs\n\t}\n\t// TODO(irbekrm): right now if two Ingress resources attempt to apply different Tailscale Service configs (different\n\t// tags, or HTTP endpoint settings) we can end up reconciling those in a loop. We should detect when an Ingress\n\t// with the same generation number has been reconciled ~more than N times and stop attempting to apply updates.\n\tif existingTSSvc == nil ||\n\t\t!reflect.DeepEqual(tsSvc.Tags, existingTSSvc.Tags) ||\n\t\t!reflect.DeepEqual(tsSvc.Ports, existingTSSvc.Ports) ||\n\t\t!ownersAreSetAndEqual(tsSvc, *existingTSSvc) {\n\t\tlogger.Infof(\"Ensuring Tailscale Service exists and is up to date\")\n\t\tif err := tsClient.VIPServices().CreateOrUpdate(ctx, tsSvc); err != nil {\n\t\t\treturn false, fmt.Errorf(\"error creating Tailscale Service: %w\", err)\n\t\t}\n\t}\n\n\t// 5. Update tailscaled's AdvertiseServices config, which should add the Tailscale Service\n\t// IPs to the ProxyGroup Pods' AllowedIPs in the next netmap update if approved.\n\tmode := serviceAdvertisementHTTPS\n\tif isHTTPEndpointEnabled(ing) || isHTTPRedirectEnabled(ing) {\n\t\tmode = serviceAdvertisementHTTPAndHTTPS\n\t}\n\tif err = r.maybeUpdateAdvertiseServicesConfig(ctx, serviceName, mode, pg); err != nil {\n\t\treturn false, fmt.Errorf(\"failed to update tailscaled config: %w\", err)\n\t}\n\n\t// 6. Update Ingress status if ProxyGroup Pods are ready.\n\tcount, err := numberPodsAdvertising(ctx, r.Client, r.tsNamespace, pg.Name, serviceName.String())\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to check if any Pods are configured: %w\", err)\n\t}","sourceCodeStart":345,"sourceCodeEnd":381,"githubUrl":"https://github.com/tailscale/tailscale/blob/cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042/cmd/k8s-operator/ingress-for-pg.go#L345-L381","documentation":"Thrown by HAIngressReconciler.maybeProvision when tsClient.VIPServices().CreateOrUpdate fails to create or update the Tailscale Service (VIPService) named svc:<hostname> on the control plane. It can also wrap upstream refusals from owner handling: an existing VIPService without the tailscale.com/owner-references annotation (likely created outside the operator) is rejected rather than adopted, as is one already owned by another resource. Tailscale Services are an alpha tailnet feature, so a tailnet without it enabled also fails here.","triggerScenarios":"Tailscale Services (VIPServices) alpha feature not enabled on the tailnet; the operator device lacks ACL permission to create VIPServices or assign the tags requested via the tailscale.com/tags annotation; control plane 5xx or network failure; an existing svc:<hostname> VIPService created by hand without/with foreign owner annotations; invalid tags or description rejected by the API.","commonSituations":"Tailnet policy not updated before adopting ProxyGroup Ingresses; operator API credentials expired or revoked; Ingress hostname collides with a manually created VIPService; annotation tags not granted to the operator device.","solutions":["Read the wrapped message: feature-not-enabled means enable Tailscale Services for the tailnet; ACL/tag errors mean update the tailnet policy so the operator can create VIPServices with those tags.","Check for a foreign VIPService named svc:<hostname> (admin console or API); if it predates the operator, rename the Ingress hostname or delete the VIPService.","Verify the operator's control-plane credentials are valid and egress to the control plane is allowed.","Transient 5xx/network failures resolve via reconcile backoff; check kubectl events on the Ingress for the outcome."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before exposing, ensure the VIPService name is free or operator-owned\nname := \"svc:\" + hostname\nif svc, err := tsClient.VIPServices().Get(ctx, name); err == nil {\n\tif svc.Annotations[\"tailscale.com/owner-references\"] == \"\" {\n\t\treturn fmt.Errorf(\"%s exists but is not operator-owned; delete it or rename the Ingress\", name)\n\t}\n}","typeGuard":"func isPolicyErr(err error) bool { // permanent: do not hot-retry\n\treturn err != nil && (strings.Contains(err.Error(), \"not permitted\") ||\n\t\tstrings.Contains(err.Error(), \"feature\") ||\n\t\tstrings.Contains(err.Error(), \"owner annotation\"))\n}","tryCatchPattern":"if err := tsClient.VIPServices().CreateOrUpdate(ctx, tsSvc); err != nil {\n\tif isPolicyErr(err) {\n\t\tr.recorder.Event(ing, corev1.EventTypeWarning, \"VIPServiceRejected\", err.Error())\n\t\treturn false, nil // surface to the user; stop error-looping\n\t}\n\treturn false, fmt.Errorf(\"error creating Tailscale Service: %w\", err) // transient: backoff requeue\n}","preventionTips":["Enable the Tailscale Services feature on the tailnet before creating ProxyGroup Ingresses.","Never create svc: VIPServices by hand; let the operator own that namespace.","Restrict the tailscale.com/tags annotation to tags the operator device is granted.","Rotate control-plane API credentials before they expire."],"tags":["tailscale","control-plane","vip-service","acl","alpha"],"backgroundTag":null,"analyzedSha":"cfe32b8be6a33f8e24fbc369cbfbf7c729d9e042","analyzedAt":"2026-08-15T19:58:31.583Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}