derailed/k9s · error
expecting Service resource
Error message
expecting Service resource
What it means
Service.GetInstance fetches the service by FQN and converts the unstructured object to the typed v1.Service. If conversion fails (payload shape does not match core/v1 Service), the typed error is discarded and this sentinel returned. Callers use it to resolve headless services in front of StatefulSets and to build port-forwards.
Source
Thrown at internal/dao/svc.go:62
svc, err := s.GetInstance(fqn)
if err != nil {
return "", err
}
return podFromSelector(s.Factory, svc.Namespace, svc.Spec.Selector)
}
// GetInstance returns a service instance.
func (s *Service) GetInstance(fqn string) (*v1.Service, error) {
o, err := s.getFactory().Get(s.gvr, fqn, true, labels.Everything())
if err != nil {
return nil, err
}
var svc v1.Service
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &svc)
if err != nil {
return nil, errors.New("expecting Service resource")
}
return &svc, nil
}
// ----------------------------------------------------------------------------
// Helpers...
func podFromSelector(f Factory, ns string, sel map[string]string) (string, error) {
oo, err := f.List(client.PodGVR, ns, true, labels.Set(sel).AsSelector())
if err != nil {
return "", err
}
if len(oo) == 0 {
return "", fmt.Errorf("no matching pods for %v", sel)
}
View on GitHub (pinned to 2d3ccc6ba2)
Solutions
- Verify the object: kubectl get svc <fqn> -o yaml must round-trip as core/v1 Service.
- Ensure the DAO was Init'ed with client.SvcGVR and the factory cache is fresh (restart k9s after upgrades).
- Wrap the conversion error (%w) to expose the failing field instead of the generic message.
Example fix
// before
if err != nil { return nil, errors.New("expecting Service resource") }
// after
if err != nil { return nil, fmt.Errorf("expecting Service resource: %w", err) } Defensive patterns
Strategy: type-guard
Validate before calling
u, ok := o.(*unstructured.Unstructured)
if !ok || u.GetKind() != "Service" {
return nil, fmt.Errorf("expected core/v1 Service, got %s/%s", u.GetAPIVersion(), u.GetKind())
} Type guard
func isService(o runtime.Object) bool {
u, ok := o.(*unstructured.Unstructured)
return ok && u.GetKind() == "Service" && u.GetAPIVersion() == "v1"
} Try / catch
svc, err := svcDAO.GetInstance(fqn)
if err != nil {
if strings.Contains(err.Error(), "expecting Service resource") { /* schema/object mismatch */ }
return err
} Prevention
- Init the Service DAO with client.SvcGVR only.
- Verify with kubectl that the object round-trips as core/v1 Service.
- Wrap conversion errors with %w when extending this code.
When it happens
Trigger: GetInstance on an FQN whose stored object is not a core/v1 Service — wrong GVR passed to Init, an object mutated by a webhook with fields the converter rejects, or schema mismatch between compiled client-go and the cluster.
Common situations: Clusters with service- mutating admission plugins; version skew between k9s and API server; custom code constructing the Service DAO against an Endpoint or ExternalName variant carrying unexpected fields.
Related errors
- expecting Statefulset resource
- expecting cronjob resource
- expecting Deployment resource
- expecting DaemonSet resource
- expecting Job resource
AI-assisted analysis of derailed/k9s@2d3ccc6ba2 (2026-08-15).
Data as JSON: /api/errors/52be76e7777b5533.
Report an issue: GitHub.