derailed/k9s · error

expecting StatefulSet resource

Error message

expecting StatefulSet resource

What it means

DaemonSet.Scan converts every DaemonSet in a namespace into appsv1.DaemonSet to find ConfigMap, Secret and PVC references (internal/dao/ds.go:178). The message is misleading: the code converts appsv1.DaemonSet but the sentinel says 'expecting StatefulSet resource' — a copy-paste from the StatefulSet scanner. Functionally it still means FromUnstructured could not fit a cached daemonsets.apps object into the compiled struct, and one bad object aborts the whole scan.

Source

Thrown at internal/dao/ds.go:178

	}

	return refs, nil
}

// Scan scans for cluster refs.
func (d *DaemonSet) Scan(_ context.Context, gvr *client.GVR, fqn string, wait bool) (Refs, error) {
	ns, n := client.Namespaced(fqn)
	oo, err := d.getFactory().List(d.gvr, ns, wait, labels.Everything())
	if err != nil {
		return nil, err
	}

	refs := make(Refs, 0, len(oo))
	for _, o := range oo {
		var ds appsv1.DaemonSet
		err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &ds)
		if err != nil {
			return nil, errors.New("expecting StatefulSet resource")
		}
		switch gvr {
		case client.CmGVR:
			if !hasConfigMap(&ds.Spec.Template.Spec, n) {
				continue
			}
			refs = append(refs, Ref{
				GVR: d.GVR(),
				FQN: client.FQN(ds.Namespace, ds.Name),
			})
		case client.SecGVR:
			found, err := hasSecret(d.Factory, &ds.Spec.Template.Spec, ds.Namespace, n, wait)
			if err != nil {
				slog.Warn("Unable to locate secret",
					slogs.FQN, fqn,
					slogs.Error, err,
				)
				continue

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. Do not search StatefulSets — the message is wrong; inspect kubectl get daemonsets.apps -n <ns> -o yaml for mistyped fields.
  2. Upgrade k9s (newer builds may fix the message and keep conversion schemas current).
  3. Fix the message locally or upstream: change ds.go:178 to 'expecting DaemonSet resource'.
  4. Wrap the converter error with %w to expose the offending field.
  5. Patch the loop to skip unparsable objects with a warning instead of aborting the scan.

Example fix

// before (ds.go:178 — message copy-pasted from sts.go)
return nil, errors.New("expecting StatefulSet resource")
// after
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &ds); err != nil {
    slog.Warn("skip unparsable daemonset", "fqn", client.FQN(u.GetNamespace(), u.GetName()), "err", err)
    continue
}
Defensive patterns

Strategy: try-catch

Validate before calling

for _, o := range oo {
    u, ok := o.(*unstructured.Unstructured)
    if !ok || u.GroupVersionKind().Kind != "DaemonSet" {
        continue
    }
    // safe to convert below
}

Type guard

func isDaemonSet(o runtime.Object) bool {
    u, ok := o.(*unstructured.Unstructured)
    return ok && u.GroupVersionKind().Kind == "DaemonSet" && u.GroupVersionKind().Group == "apps"
}

Try / catch

if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &ds); err != nil {
    slog.Warn("skip unparsable daemonset", "fqn", fqn, "err", err)
    continue // the stock message wrongly says StatefulSet; guard daemonsets
}

Prevention

When it happens

Trigger: Opening an xray or reference scan (CmGVR, SecGVR, PvcGVR) in a namespace where a stored DaemonSet fails conversion to the compiled appsv1.DaemonSet struct — version skew, GVR shadowing, or webhook-written out-of-schema fields. When debugging, ignore the StatefulSet wording and inspect daemonsets.

Common situations: Developers chasing a phantom StatefulSet problem because the message names the wrong kind; xray views failing after cluster upgrades on older k9s builds; DaemonSets mutated by legacy tooling.

Related errors


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