derailed/k9s · error
expecting ServiceAccount resource
Error message
expecting ServiceAccount resource
What it means
While resolving a workload's secret references, hasSecret follows spec.template.spec.serviceAccountName, fetches that ServiceAccount from the factory, and converts it into v1.ServiceAccount (internal/dao/dp.go:304). If the conversion fails, the sentinel 'expecting ServiceAccount resource' is returned with the real error dropped. Callers inside Deployment/DaemonSet/StatefulSet Scan only log this as a warning and skip the workload, so the scan survives, but direct callers receive the error.
Source
Thrown at internal/dao/dp.go:304
}
}
for _, s := range spec.ImagePullSecrets {
if s.Name == name {
return true, nil
}
}
if saName := spec.ServiceAccountName; saName != "" {
o, err := f.Get(client.SaGVR, client.FQN(ns, saName), wait, labels.Everything())
if err != nil {
return false, err
}
var sa v1.ServiceAccount
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &sa)
if err != nil {
return false, errors.New("expecting ServiceAccount resource")
}
for _, ref := range sa.Secrets {
if ref.Namespace == ns && ref.Name == name {
return true, nil
}
}
}
for i := range spec.Volumes {
if sec := spec.Volumes[i].Secret; sec != nil {
if sec.SecretName == name {
return true, nil
}
}
}
return false, nilView on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Inspect the referenced ServiceAccount: kubectl get serviceaccount <name> -n <ns> -o yaml and fix unexpected or mistyped fields.
- Upgrade k9s to a build matching the cluster's Kubernetes minor version.
- Confirm serviceaccounts.core is served by the core apiserver and not shadowed.
- When patching, wrap the converter error with %w so the offending field is reported.
- Optionally skip the workload with a warning instead of propagating the error out of hasSecret.
Example fix
// before
var sa v1.ServiceAccount
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &sa)
if err != nil {
return false, errors.New("expecting ServiceAccount resource")
}
// after
u, ok := o.(*unstructured.Unstructured)
if !ok {
return false, fmt.Errorf("expected unstructured serviceaccount, got %T", o)
}
var sa v1.ServiceAccount
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &sa); err != nil {
return false, fmt.Errorf("serviceaccount %q does not match v1 schema: %w", client.FQN(ns, saName), err)
} Defensive patterns
Strategy: try-catch
Validate before calling
o, err := factory.Get(client.SaGVR, client.FQN(ns, saName), wait, labels.Everything())
if err != nil { return false, err }
u, ok := o.(*unstructured.Unstructured)
if !ok || u.GroupVersionKind().Kind != "ServiceAccount" {
return false, fmt.Errorf("not a serviceaccount: %s", u.GroupVersionKind())
} Type guard
func isServiceAccount(o runtime.Object) bool {
u, ok := o.(*unstructured.Unstructured)
return ok && u.GroupVersionKind().Kind == "ServiceAccount" && u.GroupVersionKind().Group == ""
} Try / catch
var sa v1.ServiceAccount
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &sa); err != nil {
return false, fmt.Errorf("serviceaccount %q conversion: %w", saName, err)
} Prevention
- Keep ServiceAccounts untouched by custom webhooks, or validate their output against the v1 schema.
- Wrap conversion errors with %w so skipped workloads are traceable in k9s warnings.
- Check k9s logs for 'Unable to locate secret' warnings — they often hide this failure.
- Align k9s and cluster versions.
When it happens
Trigger: A secret reference scan (SecGVR branch of Scan) reaching a workload whose serviceAccountName resolves to a ServiceAccount object that does not conform to the compiled v1.ServiceAccount schema — mistyped metadata/secrets fields, an aggregated source shadowing serviceaccounts.core, or k9s/cluster version skew. Also triggered by embedding code calling hasSecret directly.
Common situations: Clusters with custom apiservers or webhooks that mutate ServiceAccounts; version skew between k9s and the apiserver; scans that warn 'Unable to locate secret' in k9s logs while silently skipping workloads whose SA fails conversion.
Related errors
- expecting Pod resource
- expecting cronjob resource
- expecting Deployment resource
- expecting DaemonSet resource
- expecting StatefulSet resource
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/e743869bae3a03ff.
Report an issue: GitHub.