derailed/k9s · error

expecting Deployment resource

Error message

expecting Deployment resource

What it means

Pod.ScanSA lists pods in a namespace and converts each object into v1.Pod to match spec.serviceAccountName (only controller-less pods are considered) for the UsedBy action on ServiceAccounts (internal/dao/pod.go:271). The message is misleading: the conversion target is v1.Pod but the sentinel says 'expecting Deployment resource' — a copy-paste from dp.go. Functionally it still means one cached pod object could not be converted, and it aborts the whole scan.

Source

Thrown at internal/dao/pod.go:271

	}

	return outs, nil
}

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

	refs := make(Refs, 0, len(oo))
	for _, o := range oo {
		var pod v1.Pod
		err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &pod)
		if err != nil {
			return nil, errors.New("expecting Deployment resource")
		}
		// Just pick controller less pods...
		if len(pod.OwnerReferences) > 0 {
			continue
		}
		if serviceAccountMatches(pod.Spec.ServiceAccountName, n) {
			refs = append(refs, Ref{
				GVR: p.GVR(),
				FQN: client.FQN(pod.Namespace, pod.Name),
			})
		}
	}

	return refs, nil
}

// Scan scans for cluster resource refs.
func (p *Pod) Scan(_ context.Context, gvr *client.GVR, fqn string, wait bool) (Refs, error) {

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. Do not chase Deployments — the message is wrong; audit kubectl get pods -n <ns> -o yaml for mistyped or unexpected fields.
  2. Upgrade k9s (newer builds match newer pod schemas and may fix the message).
  3. Fix the message locally or upstream: change pod.go:271 to 'expecting Pod resource'.
  4. Wrap the converter error with %w to expose the offending field.
  5. Patch the loop to skip unparsable pods with a warning instead of aborting the scan.

Example fix

// before (pod.go:271 — message copy-pasted from dp.go)
return nil, errors.New("expecting Deployment resource")
// after
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &pod); err != nil {
    slog.Warn("skip unparsable pod", "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 != "Pod" {
        continue
    }
    // safe to convert below
}

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: Running UsedBy on a ServiceAccount in a namespace where at least one stored Pod fails conversion to the compiled v1.Pod struct — k9s/cluster version skew (e.g. pod spec fields evolving across releases), an unusual apiserver, or sidecar-injected objects with out-of-schema fields. When debugging, ignore the Deployment wording and inspect pods.

Common situations: Developers inspecting deployments because the message names the wrong kind; reference scans failing after cluster upgrades on older k9s builds; meshes or injectors writing extra pod fields.

Related errors


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