helm/helm · error

invalid label selector: %w

Error message

invalid label selector: %w

What it means

Wraps the error from metav1.LabelSelectorAsSelector (pkg/kube/wait.go:205) during legacy --wait. The workload's spec.selector could not be converted into a valid labels.Selector — the selector in the manifest is structurally invalid (empty matchLabels AND empty matchExpressions, or malformed matchExpressions operators/values).

Source

Thrown at pkg/kube/wait.go:205

		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
	case *appsv1beta1.Deployment:
		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
	case *appsv1beta2.Deployment:
		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
	case *batchv1.Job:
		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector)
	case *corev1.Service:
		if len(t.Spec.Selector) == 0 {
			return nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name)
		}
		selector = labels.SelectorFromSet(t.Spec.Selector)

	default:
		return nil, fmt.Errorf("selector for %T not implemented", object)
	}

	if err != nil {
		return selector, fmt.Errorf("invalid label selector: %w", err)
	}

	return selector, nil
}

func (hw *legacyWaiter) watchTimeout(t time.Duration) func(*resource.Info) error {
	return func(info *resource.Info) error {
		return hw.watchUntilReady(t, info)
	}
}

// WatchUntilReady watches the resources given and waits until it is ready.
//
// This method is mainly for hook implementations. It watches for a resource to
// hit a particular milestone. The milestone depends on the Kind.
//
// For most kinds, it checks to see if the resource is marked as Added or Modified
// by the Kubernetes event stream. For some kinds, it does more:

View on GitHub (pinned to 2a29f1770b)

Solutions

  1. Read the wrapped error — it states exactly what is invalid about the selector.
  2. Fix spec.selector in the template: at least one matchLabels entry or a well-formed matchExpressions item.
  3. Validate manifests before install: helm template . | kubectl apply --dry-run=server -f -.
  4. Add a CI chart-test (helm lint plus template render) to catch empty selectors early.

Example fix

# before: conditional renders an empty selector
selector:
  matchLabels:
{{- if .Values.labels }}
{{ toYaml .Values.labels | nindent 6 }}
{{- end }}

# after: always pin the app label
selector:
  matchLabels:
    app.kubernetes.io/name: {{ include "chart.name" . }}
Defensive patterns

Strategy: validation

Validate before calling

// Validate every workload selector before install
for _, doc := range manifests {
	switch t := doc.(type) {
	case *appsv1.Deployment:
		if t.Spec.Selector == nil || (len(t.Spec.Selector.MatchLabels) == 0 && len(t.Spec.Selector.MatchExpressions) == 0) {
			return fmt.Errorf("deployment %s has empty selector", t.Name)
		}
	}
}

Try / catch

if err := installWithWait(); err != nil && strings.Contains(err.Error(), "invalid label selector") {
	// fix the workload's spec.selector and redeploy
}

Prevention

When it happens

Trigger: helm install/upgrade --wait where a Deployment/Job carries a malformed spec.selector: selector: {} (nothing set), matchExpressions using an invalid operator (e.g. 'NotIn' instead of 'NotIn' casing issues, '==' style), or templating that renders matchLabels keys with invalid characters.

Common situations: Helm template conditionals that omit selector block entirely when a value is empty; copy-paste selectors with wrong operator names; YAML indentation putting matchExpressions under the wrong key so API deserialization yields an effectively-empty selector.

Related errors


AI-assisted analysis of helm/helm@2a29f1770b (2026-08-15). Data as JSON: /api/errors/828402ecab4f7cf2. Report an issue: GitHub.