derailed/k9s · warning

no valid selector found on statefulset: %s

Error message

no valid selector found on statefulset: %s

What it means

StatefulSet.TailLogs resolves member pods via spec.selector.matchLabels before streaming logs. The error fires when the StatefulSet's selector is nil or has an empty matchLabels map, so no label query can be built. Without a selector there is no way to map the StatefulSet to its pods.

Source

Thrown at internal/dao/sts.go:84

	}

	var sts appsv1.StatefulSet
	err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &sts)
	if err != nil {
		return nil, errors.New("expecting Statefulset resource")
	}

	return &sts, nil
}

// TailLogs tail logs for all pods represented by this StatefulSet.
func (s *StatefulSet) TailLogs(ctx context.Context, opts *LogOptions) ([]LogChan, error) {
	sts, err := s.getStatefulSet(opts.Path)
	if err != nil {
		return nil, errors.New("expecting StatefulSet resource")
	}
	if sts.Spec.Selector == nil || len(sts.Spec.Selector.MatchLabels) == 0 {
		return nil, fmt.Errorf("no valid selector found on statefulset: %s", opts.Path)
	}

	return podLogs(ctx, sts.Spec.Selector.MatchLabels, opts)
}

// Pod returns a pod victim by name.
func (s *StatefulSet) Pod(fqn string) (string, error) {
	sts, err := s.getStatefulSet(fqn)
	if err != nil {
		return "", err
	}

	return podFromSelector(s.Factory, sts.Namespace, sts.Spec.Selector.MatchLabels)
}

func (s *StatefulSet) getStatefulSet(fqn string) (*appsv1.StatefulSet, error) {
	o, err := s.getFactory().Get(s.gvr, fqn, true, labels.Everything())
	if err != nil {

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. Inspect the resource: kubectl get statefulset <name> -o yaml and check spec.selector.matchLabels
  2. Fix the manifest/chart so spec.selector.matchLabels matches spec.template.metadata.labels and re-apply
  3. Until fixed, stream logs from pods directly: kubectl logs -l <correct-labels> -n <ns>

Example fix

# before: selector missing
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db
spec:
  template:
    metadata:
      labels:
        app: db
# after: valid selector
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db
spec:
  selector:
    matchLabels:
      app: db
  template:
    metadata:
      labels:
        app: db
Defensive patterns

Strategy: validation

Validate before calling

sts, err := clientset.AppsV1().StatefulSets(ns).Get(ctx, name, metav1.GetOptions{})
if err != nil { return err }
if sts.Spec.Selector == nil || len(sts.Spec.Selector.MatchLabels) == 0 {
    return fmt.Errorf("statefulset %s has no selector; fix its spec before tailing logs", name)
}
// safe to call TailLogs now

Type guard

func hasPodSelector(sts *appsv1.StatefulSet) bool {
    return sts.Spec.Selector != nil && len(sts.Spec.Selector.MatchLabels) > 0
}

Try / catch

chans, err := stsDAO.TailLogs(ctx, opts)
if err != nil {
    if strings.Contains(err.Error(), "no valid selector") {
        // surface actionable hint: workload spec is broken
        return fmt.Errorf("cannot tail logs: %w — inspect spec.selector.matchLabels", err)
    }
    return err
}

Prevention

When it happens

Trigger: Invoking TailLogs on a StatefulSet whose spec.selector is absent or empty. Valid StatefulSets rejected by kubectl apply validation can still exist when created by controllers, Helm with --force, or operators that bypass strict validation; hand-edited YAML with a typo'd or missing selector block also triggers it.

Common situations: Broken Helm charts or operators that omit spec.selector.matchLabels; StatefulSets patched in ways that stripped the selector; users pressing the log shortcut on such a malformed workload in k9s.

Related errors


AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15). Data as JSON: /api/errors/4bdf012df9c13e0b. Report an issue: GitHub.