derailed/k9s · error

expecting Deployment resource

Error message

expecting Deployment resource

What it means

k9s's Deployment DAO fetches a single Deployment from the shared informer cache as *unstructured.Unstructured and converts it into appsv1.Deployment via runtime.DefaultUnstructuredConverter.FromUnstructured (internal/dao/dp.go:96). The sentinel 'expecting Deployment resource' is returned when conversion fails; the underlying converter error is dropped, so the real offending field is invisible. It means the cached object under deployments.apps does not fit the k8s.io/api schema k9s was compiled against.

Source

Thrown at internal/dao/dp.go:96

	dp, err := d.GetInstance(fqn)
	if err != nil {
		return "", err
	}

	return podFromSelector(d.Factory, dp.Namespace, dp.Spec.Selector.MatchLabels)
}

// GetInstance fetch a matching deployment.
func (d *Deployment) GetInstance(fqn string) (*appsv1.Deployment, error) {
	o, err := d.Factory.Get(d.gvr, fqn, true, labels.Everything())
	if err != nil {
		return nil, err
	}

	var dp appsv1.Deployment
	err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &dp)
	if err != nil {
		return nil, errors.New("expecting Deployment resource")
	}

	return &dp, nil
}

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

	refs := make(Refs, 0, len(oo))
	for _, o := range oo {
		var dp appsv1.Deployment
		err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &dp)
		if err != nil {

View on GitHub (pinned to 2d3ccc6ba2)

Solutions

  1. Inspect the object: kubectl get deployment.apps <name> -n <ns> -o yaml and look for unexpected or mistyped fields; fix via kubectl edit.
  2. Upgrade k9s so its vendored k8s.io/* versions match the cluster minor version.
  3. Confirm deployments.apps is served by the core apiserver and not shadowed (kubectl api-resources --api-group=apps).
  4. Restart k9s to rebuild informer caches after cluster or webhook changes.
  5. If embedding, wrap the converter error with %w to reveal the offending field.

Example fix

// before
err = runtime.DefaultUnstructuredConverter.FromUnstructured(o.(*unstructured.Unstructured).Object, &dp)
if err != nil {
    return nil, errors.New("expecting Deployment resource")
}
// after
u, ok := o.(*unstructured.Unstructured)
if !ok {
    return nil, fmt.Errorf("expected unstructured deployment, got %T", o)
}
var dp appsv1.Deployment
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &dp); err != nil {
    return nil, fmt.Errorf("deployment %q does not match v1 schema: %w", fqn, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

o, err := factory.Get(client.DpGVR, fqn, true, labels.Everything())
if err != nil { return err }
u, ok := o.(*unstructured.Unstructured)
if !ok || u.GroupVersionKind().Kind != "Deployment" {
    return fmt.Errorf("not a deployment: %s", u.GroupVersionKind())
}

Type guard

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

Try / catch

var dp appsv1.Deployment
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &dp); err != nil {
    return nil, fmt.Errorf("deployment %q conversion: %w", fqn, err)
}

Prevention

When it happens

Trigger: Calling dao.Deployment.GetInstance(fqn) — the deployment detail view, restart, scale or image actions — when the stored object has a field whose JSON type cannot be assigned to the compiled appsv1.Deployment struct: apiserver version skew, an aggregated API or CRD shadowing deployments.apps, or webhook-patched out-of-schema values.

Common situations: Old k9s builds against new clusters (or the reverse) where deployment spec/status fields changed shape; custom mutating webhooks writing extra fields; GitOps tools force-applying legacy manifests; embedding the DAO layer with a custom factory whose cache holds converted objects.

Related errors


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