cilium/cilium · error

invalid backend-port annotation %q: %w

Error message

invalid backend-port annotation %q: %w

What it means

In the EndpointSlice reconciler's Reconcile, a Service/Gateway frontend annotated with the Cilium backend-port annotation must carry a valid 16-bit unsigned port. strconv.ParseUint fails when the value is empty, non-numeric, or out of the uint16 range, producing 'invalid backend-port annotation %q: %w'. The reconcile is failed (controllerruntime.Fail), so the object is retried until the annotation is corrected.

Source

Thrown at operator/pkg/gateway-api/endpointslice_reconcile.go:99

	if !predicates.IsManagedFrontendEndpointSlice(frontend) {
		return controllerruntime.Success()
	}

	backendRef := frontend.Annotations[gwModel.BackendServiceAnnotation]
	if backendRef == "" {
		scopedLog.WarnContext(ctx, "Managed EndpointSlice missing backend-service annotation, skipping")
		return controllerruntime.Success()
	}
	backendNs, backendName, ok := strings.Cut(backendRef, "/")
	if !ok || backendNs == "" || backendName == "" {
		scopedLog.WarnContext(ctx, "Invalid backend-service annotation",
			logfields.Annotation, backendRef)
		return controllerruntime.Success()
	}

	servicePort, err := strconv.ParseUint(frontend.Annotations[gwModel.BackendPortAnnotation], 10, 16)
	if err != nil {
		return controllerruntime.Fail(fmt.Errorf("invalid backend-port annotation %q: %w",
			frontend.Annotations[gwModel.BackendPortAnnotation], err))
	}

	backendSvc := &corev1.Service{}
	if err := r.Client.Get(ctx, types.NamespacedName{Namespace: backendNs, Name: backendName}, backendSvc); err != nil {
		if k8serrors.IsNotFound(err) {
			scopedLog.DebugContext(ctx, "Backend Service not found, clearing endpoints",
				logfields.Backend, backendRef)
			return r.patchFrontend(ctx, frontend, nil, nil)
		}
		return controllerruntime.Fail(fmt.Errorf("failed to get backend Service %s: %w", backendRef, err))
	}

	matchedPort := matchServicePort(backendSvc.Spec.Ports, uint16(servicePort), portProtocol(frontend.Ports))
	if matchedPort == nil {
		scopedLog.WarnContext(ctx, "Backend Service does not expose requested port; clearing endpoints",
			logfields.Backend, backendRef,
			logfields.Port, servicePort,

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Set the annotation to a plain numeric port in 1–65535, e.g. cilium.io/backend-port: "8080".
  2. Remove the annotation entirely if it is not needed for the backend routing setup.
  3. Fix any template/automation generating the annotation so it emits a validated integer.
  4. Correct the annotation on the offending object to clear the stuck retry loop.

Example fix

// before
metadata:
  annotations:
    cilium.io/backend-port: https
// after
metadata:
  annotations:
    cilium.io/backend-port: "443"
Defensive patterns

Strategy: validation

Validate before calling

v, ok := frontend.Annotations[gwModel.BackendPortAnnotation]
if ok {
	if _, err := strconv.ParseUint(v, 10, 16); err != nil {
		return fmt.Errorf("backend-port must be an integer in [1,65535], got %q", v)
	}
}

Type guard

func validBackendPort(v string) bool {
	p, err := strconv.ParseUint(v, 10, 16)
	return err == nil && p > 0
}

Try / catch

if _, err := strconv.ParseUint(frontend.Annotations[gwModel.BackendPortAnnotation], 10, 16); err != nil {
	return controllerruntime.Fail(fmt.Errorf("invalid backend-port annotation %q: %w",
		frontend.Annotations[gwModel.BackendPortAnnotation], err))
}
// fix the annotation, then trigger a resync

Prevention

When it happens

Trigger: A frontend object carries the backend-port annotation with a value that ParseUint(…, 10, 16) rejects: empty string, "https", "-1", "99999" (>65535), or a value containing whitespace.

Common situations: Users hand-editing annotations and typing a service/protocol name instead of a port; automation templates leaving the annotation empty; copying a port value above 65535 from another system.

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/62f01a6e434cfd91. Report an issue: GitHub.