k3s-io/k3s · error

toleration with empty key must have operator 'Exists'

Error message

toleration with empty key must have operator 'Exists'

What it means

validateToleration applies Kubernetes toleration semantics to tolerations k3s reads from the ServiceLB service annotation (servicelb.k3s.io/tolerations): the operator defaults to 'Equal', and a toleration with an empty key is only meaningful as 'tolerate everything' with operator 'Exists'. An empty key with operator Equal (explicit or defaulted) is rejected.

Source

Thrown at pkg/cloudprovider/servicelb.go:734

	}

	for i := range tolerations {
		if err := validateToleration(&tolerations[i]); err != nil {
			return nil, fmt.Errorf("validation failed for toleration %d: %v", i, err)
		}
	}

	return tolerations, nil
}

// validateToleration ensures a toleration has valid fields according to its operator.
func validateToleration(toleration *core.Toleration) error {
	if toleration.Operator == "" {
		toleration.Operator = core.TolerationOpEqual
	}

	if toleration.Key == "" && toleration.Operator != core.TolerationOpExists {
		return errors.New("toleration with empty key must have operator 'Exists'")
	}

	if toleration.Operator == core.TolerationOpExists && toleration.Value != "" {
		return errors.New("toleration with operator 'Exists' must have an empty value")
	}

	return nil
}

// generateName generates a distinct name for the DaemonSet based on the service name and UID
func generateName(svc *core.Service) string {
	name := svc.Name
	// ensure that the service name plus prefix and uuid aren't overly long, but
	// don't cut the service name at a trailing hyphen.
	if len(name) > 48 {
		trimlen := 48
		for name[trimlen-1] == '-' {
			trimlen--

View on GitHub (pinned to 6ba341e396)

Solutions

  1. For a catch-all toleration set operator Exists: `{"operator":"Exists","effect":"NoSchedule"}`
  2. Otherwise provide a concrete key: `{"key":"node-role.kubernetes.io/control-plane","effect":"NoSchedule"}`
  3. Validate the annotation JSON against core.Toleration rules before applying the Service

Example fix

# before
kubectl annotate svc my-svc servicelb.k3s.io/tolerations='[{"key":"","effect":"NoSchedule"}]'

# after
kubectl annotate svc my-svc servicelb.k3s.io/tolerations='[{"operator":"Exists","effect":"NoSchedule"}]'
Defensive patterns

Strategy: validation

Validate before calling

// validate servicelb tolerations annotation before applying the Service
func tolerationOK(t core.Toleration) bool {
    op := t.Operator
    if op == "" {
        op = core.TolerationOpEqual
    }
    if t.Key == "" && op != core.TolerationOpExists {
        return false // empty key requires Exists
    }
    if op == core.TolerationOpExists && t.Value != "" {
        return false // Exists cannot carry a value
    }
    return true
}

Type guard

func isValidServiceLBToleration(t core.Toleration) bool {
    op := t.Operator
    if op == "" { op = core.TolerationOpEqual }
    return !((t.Key == "" && op != core.TolerationOpExists) || (op == core.TolerationOpExists && t.Value != ""))
}

Prevention

When it happens

Trigger: Annotating a Service with `servicelb.k3s.io/tolerations: '[{"key":"","operator":"Equal","effect":"NoSchedule"}]'`, or omitting the operator on a toleration with no key, e.g. `[{"effect":"NoSchedule"}]`.

Common situations: Writing a catch-all toleration and forgetting that the operator must be Exists; JSON shorthand entries with only an effect field; generated annotations from templates that leave key empty.

Related errors


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/c4121cadf94d0708. Report an issue: GitHub.