derailed/k9s · error
expecting Pod resource
Error message
expecting Pod resource
What it means
Pod.Scan converts every controller-less pod in a namespace into v1.Pod to find ConfigMap, Secret and PVC references (internal/dao/pod.go:301); it backs the xray view and UsedBy on configmaps/secrets/PVCs. If any object fails conversion, the sentinel 'expecting Pod resource' aborts the whole scan and the underlying converter error is discarded.
Source
Thrown at internal/dao/pod.go:301
}
return refs, nil
}
// Scan scans for cluster resource refs.
func (p *Pod) Scan(_ context.Context, gvr *client.GVR, 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 Pod resource")
}
// Just pick controller less pods...
if len(pod.OwnerReferences) > 0 {
continue
}
switch gvr {
case client.CmGVR:
if !hasConfigMap(&pod.Spec, n) {
continue
}
refs = append(refs, Ref{
GVR: p.GVR(),
FQN: client.FQN(pod.Namespace, pod.Name),
})
case client.SecGVR:
found, err := hasSecret(p.Factory, &pod.Spec, pod.Namespace, n, wait)
if err != nil {
slog.Warn("Locate secret failed",View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Audit pods in the namespace (kubectl get pods -o yaml) for unexpected or mistyped fields; fix injector templates or manifests.
- Upgrade k9s so its vendored k8s.io/* versions match the cluster minor version.
- Verify pods.core is served by the core apiserver and not shadowed.
- Restart k9s to rebuild informer caches.
- Patch the loop to skip unparsable pods with a warning so one object cannot kill the scan.
Example fix
// before
if err != nil {
return nil, errors.New("expecting Pod resource")
}
// after (scan loops: degrade per object)
if err != nil {
slog.Warn("skip unparsable pod", "index", i, "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", "err", err)
continue
} Prevention
- Treat pod-scan conversions as per-object warnings, not fatal errors.
- Validate mutating webhooks (meshes, sidecar injectors) against the v1 Pod schema.
- Keep k9s's k8s.io/api in sync with the cluster minor version.
- Wrap converter errors with %w to keep the offending field visible.
When it happens
Trigger: Opening an xray or reference scan (CmGVR, SecGVR, PvcGVR) in a namespace where one cached pod object cannot convert to the compiled v1.Pod struct — k9s/cluster version skew (pod spec fields changing across Kubernetes releases), injector webhooks writing out-of-schema fields, or an aggregated source shadowing pods.core.
Common situations: xray or UsedBy views failing wholesale after cluster upgrades on older k9s builds; service meshes or mutating sidecar injectors adding non-schema fields to pods; environments running mixed Kubernetes versions.
Related errors
- expecting ServiceAccount resource
- expecting Deployment resource
- expecting cronjob resource
- expecting Deployment resource
- expecting DaemonSet resource
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/8428fd2d8fcb6ead.
Report an issue: GitHub.