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
- Read the wrapped error — it states exactly what is invalid about the selector.
- Fix spec.selector in the template: at least one matchLabels entry or a well-formed matchExpressions item.
- Validate manifests before install: helm template . | kubectl apply --dry-run=server -f -.
- 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
- helm lint and server-side dry-run in CI to catch malformed selectors.
- Template guards: fail chart rendering if selector blocks would be empty.
- Use immutable selectors pinned to a stable label key.
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
- invalid service '%s': Service is defined without a selector
- metadata.name and metadata.generateName cannot both be set
- HELM_ERR_START%sHELM_ERR_END
- unknown wait strategy (s%s). Valid values are: watcher, hook
- invalid release name, must match regex %s and the length mus
AI-assisted analysis of helm/helm@2a29f1770b (2026-08-15).
Data as JSON: /api/errors/828402ecab4f7cf2.
Report an issue: GitHub.