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

  1. Verify the object: kubectl get svc <fqn> -o yaml must round-trip as core/v1 Service.
  2. Ensure the DAO was Init'ed with client.SvcGVR and the factory cache is fresh (restart k9s after upgrades).
  3. 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

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


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